Codeforces 909 substr用法 思维合并线段目标最少 Py语句逆推DP vecrtor缩点删不同颜色点模拟 拓扑排序处理任务
A
str.substr(i,j) 从str[i]开始起取j个字符作为返回的字符串
/* Huyyt */ #include <bits/stdc++.h> using namespace std; typedef long long ll; int main() { string a, b; cin >> a >> b; string ans = "zzzzzzzzzzzzzzzzzzzzzzzzzz"; string now; for (int i = 1; i <= a.size(); i++) { for (int j = 1; j <= b.size(); j++) { now = a.substr(0, i) + b.substr(0, j); ans = min(now, ans); } } cout << ans << endl; return 0; }
B
解:
观察到只有当一个地方重复的时候两个线段无法合并
所以有个结论就是最终合并完后的线段数是无法合并数量最多的长度为1的线段数
/* Huyyt */ #include <bits/stdc++.h> using namespace std; typedef long long ll; int visit[105]; int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { for (int j = 1; j <= n - i + 1; j++) { for (int k = j; k <= j + i - 1; k++) { visit[k]++; } } } int anser = 0; for (int i = 1; i <= n; i++) { anser = max(anser, visit[i]); } cout << anser << endl; return 0; }
C
S表示一个一般语句 F表示一个循环 循环体内不能为空
问你最后能有几个合法的程序
解:
①dp[i][j]表示到第i个字母该字母在第j层的方案数(层数即for循环的层数)
1.当前面一个操作是for循环时 因为for循环不能为空 所以当前操作必须加在前面一个for循环的后面
2.当前面一个操作是s时 不管后续操作是f还是s最高层数都不会变(即加s或者加f都与前一个s在同一个for循环中)
但是假设你当前加的是第X层 你可以加的地方就是X到最高层的所有方案数之和 所以倒着推.
最后答案就是第n个操作在第1-n层方案数之和 注意当最后一个操作是f时是不合法的 答案为0
/* Huyyt */ #include <bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define pb push_back using namespace std; typedef long long ll; const long long mod = 1e9 + 7; const int maxn = 5010; int n, f; char op[maxn][5]; ll dp[maxn][maxn]; int main() { scanf("%d", &n); for (int i = 1; i <= n; i ++) { scanf("%s", op[i]); } dp[1][1] = 1; for (int i = 2; i <= n; i ++) { if (op[i - 1][0] == 'f') { for (int j = 1; j <= i - 1; j ++) { dp[i][j + 1] = dp[i - 1][j]; } } else { ll sum = 0; for (int j = i - 1; j >= 1; j --) { sum = (sum + dp[i - 1][j]) % mod; dp[i][j] = sum; } } } ll ans = 0; if (op[n][0] == 'f') { ans = 0; } else { for (int j = 1; j <= n; j ++) { ans = (ans + dp[n][j]) % mod; } cout << ans << endl; } return 0; }
D
给你一条直线 直线上有N个点 每个点有一个小写字母表示它的颜色 每次操作你可以任选几个邻居颜色与自身不同的点将他们删掉
问你最少需要几次操作使得最终得到点集不能进行操作
N<=1E6
解:
剩下的点集不能进行操作有两种情况 ①集合为空 ②集合不为空且集合中的字母全部一样
所以题目就转变成问你最少多少次操作能使得点全部删完或者剩下的点颜色全部一样
继续观察可以知道 当有连续的一段相同字母 我们最多只能删掉这连续一段两端的两个
所以缩点 每次让左边界与右边界的size-1 其他的size-min(size,2) 每删完一次后去除掉size为0的即可
/* Huyyt */ #include <bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #define mkp(a,b) make_pair(a,b) #define pb push_back using namespace std; typedef long long ll; const long long mod = 1e9 + 7; int len; vector<pair<int, int> > v; int num[1000005]; int main() { string now; cin >> now; len = now.size(); for (int i = 0; i < len; i++) { num[i] = now[i] - 'a'; } for (int i = 0; i < len; i++) { if (!v.size()) { v.push_back(mkp(num[i], 1)); } else { if (v[v.size() - 1].first == num[i]) { v[v.size() - 1].second++; } else { v.push_back(mkp(num[i], 1)); } } } int anser = 0; while (v.size() > 1) { anser++; vector<pair<int, int> > cnt; for (int i = 0; i < v.size(); i++) { if (i == 0 || i == v.size() - 1) { v[i].second--; } else { v[i].second -= 2; } } for (auto it : v) { if (it.second <= 0) { continue; } else { if (cnt.empty()) { cnt.push_back(it); } else if (it.first == cnt[cnt.size() - 1].first) { cnt[cnt.size() - 1].second += it.second; } else { cnt.push_back(it); } } } v = cnt; } cout << anser << endl; return 0; }
E