Codeforces 909 A-F
CF909 题解
题目链接
题解
A
题目翻译:给定两个字符串,求字典序最小的“两字符串非空前缀拼接形成的字符串”。
算法标签:贪心
题目分析:
字典序最小,即从左往右依次比较字符,直到一方不剩字符或两字符不同。因此想到贪心。由于前缀非空,因此在前一字符串上不断输出,直到输出结束或字符大于后一字符串的第一个字符。
代码略。
B
题目翻译:
输入
,由此得到 个线段。你可以将它们拼接在一起,但要注意不能改变它们的左右端点位置。求拼接形成的线段最小数量。下图为当 时的最优解。
算法标签:数学、构造、贪心
题目分析:本题有多种解题思路。
- 打标找规律(其一)
若将答案按照 的大小排成一个数列,则该数列的前几项为:
0, 1, 2, 4, 6, 9, …
不难发现答案为 直接做就做完了。
#include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; n++; cout << floor(n / 2.0) * ceil(n / 2.0) << endl; return 0; }
- 打标找规律(其二)
不难发现答案有规律 。直接做就做完了。
#include <bits/stdc++.h> using namespace std; int main() { int n; cin >> n; if (n == 1) puts("1"); else if (n == 2) puts("2"); else if (n == 3) puts("4"); else if (n == 4) puts("6"); else { int a = 1, b = 2, c = 4, d = 6, e; n -= 4; while (n--) { e = 2 * d - 2 * b + a; a = b; b = c; c = d; d = e; } printf("%d\n", d); } return 0; }
- 对规律进行证明
因为规律可能错误,为了避免罚时(CF 赛制)和挂分(OI 赛制),需要对规律进行证明。
以下内容翻译自官方题解:
考虑长度为
的段 。显然,覆盖此段的所有段必须属于不同的层。为了覆盖它,线段的左端必须位于点 (共 种选择),而右端分别在点 (共 种选择)。所以覆盖 的段数等于 。在所有 中, 的最大值给出了层数的下界。
由于该问题不需要显式构造,我们可以猜测这个界限是精确的。最大值可以在 内找到;或者,可以看出,当 为奇数时,最大值出现在中间段,而当 为偶数时,最大值出现在两个中间段之一。
所以答案是。
我们也可以通过一个明确的构造来证明这一点。将所有线段按照其左端点的非降序排列,然后再按照其右端点的升序排列。尝试贪心地为每个下一段找到一个位置:如果是当前线段的左端点,且线段 在某一层是空闲的,则将当前线段添加到该层;否则,用当前线段开始一个新的层。
是的,这就是个的问题! (滑稽)
C
题目翻译:
题目给定一段类 Python 代码(只有两种语句:
p
(代表f
(代表for
)),要求计算出这段代码有多少种合法的缩进。答案对取模。
算法标签:动态规划
题目分析:类 Python 语言的缩进规则如下:
- 第一条语句不加缩进
- 对于其他语句有:
- 若其为某条
for
的循环体时,加该for
语句缩进的下一级 - 否则不缩进
- 若其为某条
- 特别的,每条
for
都必须有至少一条语句作为循环体
这是一个例子:
print print for print for print print print print
于是可以想到动态规划。设
显然当
#include <bits/stdc++.h> using namespace std; int dp[5010][5010], sum[5010], n, cnt = 0; string s, lst; const int mod = 1e9 + 7; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> s; lst = s; dp[1][0] = 1; for (int i = 2; i <= n; i++) { cin >> s; if (lst == "f") { for (int j = 1; j <= i; j++) { dp[i][j] = dp[i - 1][j - 1]; } } else { for (int j = i; j >= 0; j--) { dp[i][j] = (dp[i][j + 1] + dp[i - 1][j]) % mod; } } lst = s; } int ans = 0; for (int i = 0; i <= n; i++) ans = (ans + dp[n][i]) % mod; cout << ans << "\n"; return 0; }
D
题目翻译:
给定一条直线上的一组点。每个点都有一个指定的颜色。对于点
,它的邻居是在它们和 之间没有其他点的点。每个点最多有两个邻居——一个在左边,一个在右边。
对这组点执行一系列操作。在一个操作中,你删除所有有至少一个不同颜色的相邻点的点。同时删除点,即首先决定要删除哪些点,然后删除它们。之后,您可以执行下一个操作等。如果操作不会删除任何点,则不能执行该操作。
您需要执行多少次操作,直到下一个操作没有任何要删除的点?
算法标签:优化暴力
题目分析:如果暴力删点,总复杂度
考虑将字符相同的区间“缩点”。由于每次删除的是有一个不同颜色的相邻点的点,所以对于长度
注:下面的代码因为 CF 网络故障,暂时未确定是否能够通过本题。
#include <bits/stdc++.h> using namespace std; string s; int a[1000010], cnt = 1; char c[1000010]; int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> s; a[1] = 1; c[1] = s[0]; for (int i = 1; i < s.size(); i++) { if (s[i] == s[i - 1]) { a[cnt]++; } else { a[cnt + 1] = 1; c[cnt + 1] = s[i]; cnt++; } } int ans = 0; while (cnt > 1) { for (int i = 1; i <= cnt; i++) { if (i == 1 || i == cnt) a[i]--; else a[i] -= min(2, a[i]); // 当区间内点数不足时全部删除 } int tmp = 0; for (int i = 1; i <= cnt; i++) { // 去除不存在的区间 && 合并区间 if (a[i] == 0) continue; if (c[i] == c[tmp]) c[tmp] += c[i]; else { tmp++; c[tmp] = c[i]; a[tmp] = a[i]; // copy } } ans++; cnt = tmp; } cout << ans << '\n'; return 0; }
E
题目翻译:
算法标签:
题目分析:
F
题目翻译:
算法标签:
题目分析:
本文来自博客园,作者:cwkapn,转载请注明原文链接:https://www.cnblogs.com/chenaknoip/p/18534352
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)