2025牛客寒假算法基础集训营2部分题解(蒟蒻版)
1 前言:
大学初涉ACM,如今已经第二次参加牛客寒假集训营力.想当初第一次参加时,完全零经验,一次只能做出5,6题,排个1000开外。如今再来,也有十道题左右力(感觉今年更简单了捏)。
新的一年,大家也要KiraKiraDokiDoki哦!
2 题解
A 一起奏响历史之音!
签到题。用map检查输入数据是否合法即可。(由于有队友极其痛恨map,所以直接写if在读入时判断也行)
void solve()
{
int ok = 1;
int tmp;
for (int i = 1; i <= 7; i++)
{
cin >> tmp;
if (tmp == 4 || tmp == 7)
{
ok = 0;
}
}
if (ok == 0)
{
cout << "NO" << endl;
}
else
{
cout << "YES" << endl;
}
}
B 能去你家蹭口饭吃吗
签到题。按碗的容量排序好后选择第
void solve()
{
int n;
cin >> n;
vector<int>arr(n + 5);
for (int i = 1; i <= n; i++)
{
cin >> arr[i];
}
sort(arr.begin() + 1, arr.begin() + 1 + n);
int ans = arr[n / 2 + 1] - 1;
cout << ans << endl;
}
C 字符串外串
构造题,单独出现需要一些注意力,不过D题给了提示后就很简单了。题意转化后是:对26个字符求——相同字符从前往后的倒数第二个字符的下标,从后往前的倒数第二个字符的reverse下标中大的值——中最大的值记为
既然是构造题,先分情况:
-
一定不可能构造成功的情况:因为要求
为不连续子序列所以 最大为 。所以m==n一定不成立。 -
一定可以的情况:m和n接近,可以用形如
abcdfgaaaaaaaaaaaaahijklma
的形式满足 -
检查构造限制:已知m和n相近更容易得到解,那么对于构造优先考虑m最小时应该怎么构造。既然每个字符都单独计算答案并取最大值,那么最简单的办法就是要么确保一个字符一定比其他的大,要么所有字符的答案都是期望的答案。
-
考虑可能的解法:显然对于确保一个字符一定比其他的大,其m不是最小情况,因为所有字符的答案都是期望的答案有形如
abcdabcdabcda
的字符串可以相比apaaaaaaaqa
有更优的m/n比,更满足题目限制。所以选择所有字符的答案相同的情况考虑构造。 -
构造情况:想要所有字符的答案相同,只要选择适合长度的无重复字符串copy,paste就可以保证,且无重复字符串的长度越长,m/n比就越小。那非法条件也出来了,应为无重复最多26个字符,那题目m/n比需求无重复字符串的长度超出26的就一定不可能。
既然是构造题,那就按思路写好,狠狠地交一发试试长崎罚时导致的。结果真对了,不过实际来说应该检验一下是否覆盖了全部情况的。
string abc = "abcdefghijklmnopqrstuvwxyz";
void solve()
{
int n, m;
cin >> n >> m;
if (n == m )
{
cout << "NO" << endl;
return;
}
int delta = n - m;
if (delta > 26)
{
cout << "NO" << endl;
return;
}
cout << "YES" << endl;
string ans;
for (int i = 1; i <= n; i++)
{
ans.push_back('a' + i % (delta));
}
cout << ans;
cout << endl;
}
D 字符串里串
注意力题,简单题。注意到测试样例里abcc
只有最后一个需要分离构成不连续子序列,那考虑对26个字符求——相同字符从前往后的倒数第二个字符的下标,从后往前的倒数第二个字符的reverse下标中大的值——中最大的值即可。
map<char, pair<int, int>>pre;
map<char, pair<int, int>>rev;
void solve()
{
int n;
cin >> n;
string str;
cin >> str;
str = "@" + str +'@';
int ans = 0;
for (int i = 1; i <= n; i++)
{
int nowch = str[i];
if (pre[nowch].first == 0)
{
pre[nowch].first = i;
}
else if(pre[nowch].second == 0)
{
pre[nowch].second = i;
}
else
{
pre[nowch].first = pre[nowch].second;
pre[nowch].second = i;
}
}
for (char i = 'a'; i <= 'z'; i++)
{
if (pre[i].second != 0)
{
ans = max(ans, pre[i].first);
}
}
reverse(str.begin(), str.end());
for (int i = 1; i <= n; i++)
{
int nowch = str[i];
if (rev[nowch].first == 0)
{
rev[nowch].first = i;
}
else if (rev[nowch].second == 0)
{
rev[nowch].second = i;
}
else
{
rev[nowch].first = rev[nowch].second;
rev[nowch].second = i;
}
}
for (char i = 'a'; i <= 'z'; i++)
{
if (rev[i].second != 0)
{
ans = max(ans, rev[i].first);
}
}
cout << ans << endl;
}
E 一起走很长的路!
队友做了,待会PO上来。
F 一起找神秘的数!
简单数论。
设
对
对
所以
当且仅当
所以直接取
void solve()
{
int l, r;
cin >> l >> r;
int ans = r - l + 1;
cout << ans << endl;
}
G 一起铸最好的剑!
签到题,不过队友甚至罚了两发。从一开始递增遍历m的幂直到满足题意即可,记得考虑
void solve()
{
int n, m;
cin >> n >> m;
int now = m;
int delta = abs(n - m);
int ans = 1;
for (int i = 2; i <= 30; i++)
{
now = now * m;
int tmpdelta = abs(n - now);
if (tmpdelta < delta)
{
delta = tmpdelta;
ans = i;
}
else
{
break;
}
}
cout << ans << endl;
}
H 一起画很大的圆!
几何题?注意力题!卡我半天,其实在一个矩形上,要使做的圆面积最大,也就是半径最大。先取任意两点连线,并作中垂线,再取第三点,与其他两点连线作中垂线,找到圆心,摆弄一下就注意到,似乎两点间对角线最大,然后取一个尽可能接近的第三点垂下去半径最大?但这样两垂线间夹角还可以再缩小,取矩形左上角和右上角减一格,再取长边侧与左上角距离为一的点才是最优。
没有证明,只有摆弄。。。
void solve()
{
int a, b, c, d;
cin >> a >> b >> c >> d;
int len = b - a + 1;
int high = d - c + 1;
pair<int, int>lu = make_pair(d, a);
pair<int, int>rd;
if (len > high)
{
rd = make_pair(d - 1, b);
}
else
{
rd = make_pair(c, a+1);
}
cout << lu.second << " " << lu.first << endl;
cout << rd.second << " " << rd.first << endl;
if (len > high)
{
cout << lu.second + 1 << " " << lu.first<<endl;
}
else
{
cout << lu.second << " " << lu.first -1 <<endl;
}
}
J 数据时间?
纯模拟。已黑化,不会再爱了。
int ansa = 0, ansb = 0, ansc = 0;
bool bet(int hh, int mm, int ss, int start_h, int start_m, int start_s, int end_h, int end_m, int end_s) {
int now = hh * 3600 + mm * 60 + ss;
int start_time = start_h * 3600 + start_m * 60 + start_s;
int end_time = end_h * 3600 + end_m * 60 + end_s;
if (start_time > end_time)
{
return now >= start_time || now <= end_time;
}
return now >= start_time && now <= end_time;
}
map<int, map<string, int>>timesection_id;
string uid;
string ymd;
string hms;
void f(const std::string& hms) {
int hh = 0, mm = 0, ss = 0;
char de;
std::istringstream timeStream(hms);
timeStream >> hh >> de >> mm >> de >> ss;
if (bet(hh, mm, ss, 7, 0, 0, 9, 0, 0) || bet(hh, mm, ss, 18, 0, 0, 20, 0, 0))
{
timesection_id[1][uid]++;
}
else if (bet(hh, mm, ss, 11, 0, 0, 13, 0, 0))
{
timesection_id[2][uid]++;
}
else if (bet(hh, mm, ss, 22, 0, 0, 1, 0, 0))
{
timesection_id[3][uid]++;
}
}
void solve()
{
int n;
string h, m;
cin >> n >> h >> m;
if (h.size() == 1)
{
h = "0" + h;
}
if (m.size() == 1)
{
m = "0" + m;
}
map<string, int>bolls;
for (int i = 1; i <= n; i++)
{
cin >> uid >> ymd >> hms;
string tmpsum = uid + ymd + hms;
if (bolls[tmpsum] != 0)
{
continue;
}
else
{
bolls[tmpsum]++;
}
int check = 1;
for (int j = 0; j < 4; j++)
{
if (h[j] != ymd[j])
{
check = 0;
break;
}
}
for (int j = 5; j < 7; j++)
{
if (m[j-5] != ymd[j])
{
check = 0;
break;
}
}
if (check == 1)
{
f(hms);
}
}
cout << timesection_id[1].size() << " " << timesection_id[2].size() << " " << timesection_id[3].size() << endl;
}
K 可以分开吗?
dfs,经典老题。建个操作用图,为每个连通分量标记好编号即可。
int n, m;
int croods[505][505];
int usecr[505][505];
int cnt = 10;
pair<int, int> wasd[4] = { {-1, 0},{0 ,-1},{1, 0},{0, 1} };
map<int, int>cntcost;
int legalij(int nowi, int nowj)
{
if (nowi > 0 && nowi <= n)
{
if (nowj > 0 && nowj <= m)
{
return 1;
}
}
return 0;
}
int addcost(int nowi, int nowj)
{
for (int i = 0; i <= 3; i++)
{
int newi = nowi + wasd[i].first;
int newj = nowj + wasd[i].second;
if (legalij(newi, newj))
{
if (usecr[newi][newj] !=0 && usecr[newi][newj] != cnt+100005 && usecr[newi][newj]>= 100005)
{
usecr[newi][newj] = cnt + 100005;
cntcost[cnt]++;
}
}
}
return 1;
}
int ans = INF;
int dfs(int nowi, int nowj)
{
usecr[nowi][nowj] = cnt;
addcost(nowi, nowj);
for (int i = 0; i <= 3; i++)
{
int newi = nowi + wasd[i].first;
int newj = nowj + wasd[i].second;
if (legalij(newi, newj) && usecr[newi][newj]!=cnt && usecr[newi][newj] < 100005)
{
dfs(newi, newj);
}
}
return 114;
}
void solve()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
string tmp;
cin >> tmp;
for (int j = 1; j <= m; j++)
{
if (tmp[j - 1] == '0')
{
croods[i][j] = 100005;
}
else
{
croods[i][j] = 1;
}
usecr[i][j] = croods[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
if (usecr[i][j] == 1)
{
cnt++;
dfs(i, j);
ans = min(ans, cntcost[cnt]);
}
}
}
cout << ans << endl;
}
3 总结
难度偏简单,但过年氛围使得大家都挺懒散。不敢想现在都这样摸鱼,下一场在年前会寄成什么样子。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!