2024湖南第一师范学院程序设计新生赛 题解----YHW
A 七分之一的秘密
签到题 按照思路编程即可 需要注意的是题目中被7整除这个条件只有第一个数需要去判断 在判断数位是否包含7时不需要判断/10后的数能否被7整除
代码如下
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n;
cin >> n;
int q = n;
while (q)
{
if ((q % 10) == 7)
{
cout << "YES";
return 0;
}
q /= 10;
}
if (n % 7 == 0)
{
cout << "YES";
return 0;
}
cout << "NO";
return 0;
}
B 小p的文件目录
本题对于新生较难,设置在B题其实是为了让大家知道,acm不要从前往后做,要根据难度选择做题顺序,不要死磕题目。不难看出,实际上题意就是/与/之间的是目录的内容,而..出现时会删除上一级目录,/////这种连续出现的情况不需要特殊处理,我们只要在/出现就把之前出现的目录保存就好,然后处理遇到..删掉前一级目录的情况,具体看代码注释,这题用STL双端队列比较好处理,也可以试试string数组加单指针的做法。
#include <bits/stdc++.h>
using namespace std;
deque h; // 定义双端队列 可以从两端插入删除
string s, temp;
int main()
{
getline(cin, s); // 读入整行字符串
for (int i = 0; i < s.size(); i++)
{
if (s[i] >= 'a' && s[i] <= 'z')
temp += s[i]; // 用空串记录遇到的目录内容(小写字母)
if (s[i] == '/' && temp != "\0")
{ // 当遇到目录截止符号且temp内有目录内容就写入双端队列中
h.push_back(temp);
temp = ""; // 清空临时字符串
}
if (s[i] '.' && s[i + 1] '.' && !h.empty())
h.pop_back(); // 出现连续..就清除上一级目录
}
if (h.empty())
{ // 特殊情况 如果h为空要输出第一个/
cout << '/';
return 0;
}
while (!h.empty())
{ // 从前往后输出 最后一个/不用打
cout << '/';
cout << h.front();
h.pop_front();
}
return 0;
}
C 小qiの温柔
思维题,看到10的10的6次方就要想到这题不能把x当成正整数看,可以把x的每一位看做一个字符,x用字符串去读入,“把这个数看成n个正整数的和”也就是用n个正整数相加的到x,这些正整数只包含0和1,那么我们列个竖式尝试相加就能知道,最多加9次就能加完,实际上问题是找出给你的数字中哪个是最大的数,我们直接比较字符的大小就行了,字符的大小依旧符合数字的递增‘0’是32 ‘9’是41,中间依次递增
#include <bits/stdc++.h>
using namespace std;
int main()
{
string x;
char maxx = '0';
cin >> x;
for (auto m : x)
{
if (m > maxx)
maxx = m;
}
cout << maxx;
return 0;
}
D 暴力枚举
理事长还是仁慈了,本题本来考察二分查找,但是由于log2(x)在x->10e18时还是不太大,所以开long long从后往前暴力递减就能解决题目
#include <bits/stdc++.h>
using namespace std;
int main()
{
long long m, i;
cin >> m;
for (i = m; i > 0; i--)
{
if (m == i + int(log(i) / log(2)))
break;
}
cout << i;
return 0;
}
E 小红给你签到题?
可恶的理事长,这不是真的签到题,读题发现ai-aj加了绝对值,所以i<j这个条件其实没有意义,那么题目转化为这样,给你的数列中用最大的数减去最小的数,如果大于k,那么最大价值就是最大数,但是如果最大的数减最小的数差值仍然不大于k,这个时候不要以为最大价值是最小的数,我们要采取摆烂策略,用最大的数减去第二大的数,虽然差值不可能大于k,但是可以保证这两个数里最小的也是第二大的数,所以解决这个题我们只需要数列中的三个数,最大的数,第二大的数,最小的数,问题就能解决,但是n只给到10e5,因此我直接用sort快速排序省事了。
#include <bits/stdc.h>
using namespace std;
int a[100010];
int main()
{
int n, k;
cin >> n >> k;
for (int i = 1; i <= n; i)
{
cin >> a[i];
}
sort(a + 1, a + 1 + n);
if (a[n] - a[1] > k)
cout << a[n];
else
cout << a[n - 1];
return 0;
}
F 部落冲突
可恶的数学,首先瓦解题意,最小的凸多边形就是三角形,所以本题题意为给你n个点,让你去找出一个最小的三角形,直接三重循环暴力枚举即可,当然,这里需要已知坐标上三点求三角形面积公式,大家高考完没多久应该记得,我是赛场上现推的,花了不少时间,然后题目要最小面积的两倍,最后那个除以2就不用除了,无法组成三角形的情况要特殊处理一下,如果所有点都在一条线上就会导致-1。
#include <bits/stdc++.h>
using namespace std;
long long a[1000], b[1000], sm = 9e18;
long long csgo(int i, int j, int k)
{
long long x1 = a[i], y1 = b[i];
long long x2 = a[j], y2 = b[j];
long long x3 = a[k], y3 = b[k];
// int z,x,c;
if ((y2 - y1)(x3 - x2) == (y3 - y2)(x2 - x1))
return 0;
long long s = abs((x1 - x3) * y2 + (x2 - x1) * y3 + (x3 - x2) * y1);
return s;
}
int main()
{
long long m, i, n, sum = 0;
cin >> n;
for (int i = 1; i <= n; i)
cin >> a[i] >> b[i];
for (int i = 1; i <= n; i)
for (int j = i + 1; j <= n; j++)
for (int k = j + 1; k <= n; k++)
{
if (csgo(i, j, k) != 0)
sm = min(csgo(i, j, k), sm);
}
if (sm == 9e18)
cout << -1;
else
cout << sm;
return 0;
}
G Y染色体,分裂!
经典回文子串问题 这里给大家看几种解法
1 从中间枚举回文子串 要分奇数串和偶数串的情况
#include <bits/stdc++.h>
using namespace std;
int main()
{
int t, maxx = 1;
string x;
cin >> x;
for (int i = 1; i < x.size() - 1; i++)
{
if (x[i - 1] == x[i + 1])
{
t = 1;
int temp = 1;
while (i - t >= 0 && i + t < x.size() && x[i + t] == x[i - t])
{
temp += 2;
t++;
}
if (temp > maxx)
maxx = temp;
}
if (x[i] == x[i + 1])
{
t = 1;
int temp = 2;
while (i - t >= 0 && i + t + 1 < x.size() && x[i + t + 1] == x[i - t])
{
temp += 2;
t++;
}
if (temp > maxx)
maxx = temp;
}
}
cout << maxx;
return 0;
}
2 动态规划
#include <bits/stdc++.h>
using namespace std;
string longestPalindrome(string s);
int main()
{
string s;
cin >> s;
cout << longestPalindrome(s).size();
return 0;
}
string longestPalindrome(string s)
{
int len = s.length();
int *dp = new int[len + 1];
for (int i = 0; i < len + 1; i++)
dp[i] = new int[len + 1];
for (int i = 0; i < len + 1; i++)
{
for (int j = 0; j < len + 1; j)
{
dp[i][j] = 0;
}
}
for (int j = 0; j < len; j)
{
dp[j][j] = 1;
dp[j + 1][j] = 1; // 这里也就是说的要手动初始话的这种情况
}
int l = 0, r = 0;
for (int i = 1; i < len; i++)
{
for (int j = 0; j + i < len; j++)
{
if (s[j] == s[i + j] && dp[j + 1][i + j - 1])
{ // 为了保证j+1 <= i+j-1, i >= 2那么将i也就是长度为1的手动初始化
l = j;
r = j + i;
dp[l][r] = 1;
}
}
}
string str = "";
for (int i = l; i <= r; i++)
str += s[i];
return str;
delete dp;
}
最暴力的是 On三次方的解法 枚举两个端点 求中间的字符串是不是回文串 这里就不展示代码了
H Hard Math
签到题 数列求和 我比较懒 写了个前缀和的方法 大家参考即可
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, t, q[100005] = {}, w[100005] = {};
for (int i = 1; i <= 100000; i++)
{
q[i] = i;
w[i] = q[i] + w[i - 1];
}
cin >> t;
while (t--)
{
int l, r;
cin >> l >> r;
cout << w[r] - w[l - 1] << '\n';
}
return 0;
}
I 强迫症犯了,烦!
思维题,删除ai,再将ai+aj放入数列,如何让ai+aj一定不一样呢,在每个数都大于0的情况下很好解决,直接令aj是数列中最大的数,再加一个ai一定不会和其他的数相同,所以题目转变为求相同数的个数,每次将两个相同数变为一个需要1次操作,所以我们求出sum个第一个数,用n-sum就能得到重复数的个数,这里使用了STLmap,如果这个数没有出现过,在map里的值就为0,出现了一次之后我们就标1,这样就可以得到sum。
#include <bits/stdc.h>
using namespace std;
map<int, int> a;
int main()
{
long long m, i, n, sum = 0;
cin >> n;
for (int i = 1; i <= n; i)
{
cin >> m;
if (a[m] == 0)
sum++;
a[m] = 1;
}
cout << n - sum;
return 0;
}
J 你会博弈论吗?
思维题 根据题意明显暗示 我们用字符串来储存这1000个数字 由题意可知,数字从左到右可组成十进制数,因此问题可简化为一个数字字符串从0开始递增,每次在字符串中搜索有没有这个数字字符串,第一个没有的就是题意中最小的十进制非负整数。
#include <bits/stdc.h>
using namespace std;
int main()
{
long long m, i, n, sum = 0;
char c[1005], x;
string s = "";
cin >> n;
for (int i = 1; i <= n; i)
{
cin >> x;
if (x != ' ')
s += x;
}
i = 0;
while (1)
{
if (s.find(to_string(i)) == -1)
{
cout << i;
return 0;
}
i++;
}
}
K XCPCの崛起
钝感力,去生成这个所需的序列,要确定一个不确定的m和一个不确定的序列,这压根没法实现,所以时间复杂度O(1)的通解肯定存在,诶?,这个m为什么要>=2呢,这说明无论k是个什么,m必定能等于2,那m带进去试一下,哦豁,a0的a1次方加上a1的a0次方,两个加起来等于n,n-1和1秒了,试着提交一发,过辣!!!!。
#include <bits/stdc++.h>
using namespace std;
int main()
{
unsigned long long n, k;
cin >> n >> k;
cout << 2 << '\n';
cout << n - 1 << ' ' << '1';
return 0;
}