Codeforces Round #687 (Div. 2, based on Technocup 2021 Elimination Round 2) A-E
题目来源
https://codeforces.ml/contest/1457
A. Prison Break
给一个n*m的矩阵,问这个矩阵上到(a, b)距离最远的距离是多少?
思路分析
找四个角即可。
#include <bits/stdc++.h> using namespace std; int main(){ int t; scanf("%d", &t); while(t --){ int ans = 0; int n, m, r, c; scanf("%d%d%d%d", &n, &m, &r, &c); ans = max(n - r, r - 1) + max(m - c, c - 1); printf("%d\n", ans); } return 0; }
B. Repainting Street
给一个数字序列,每次可以将长度为k的区间内所有的数字进行操作(任意换或者不换数字),问最少需要多少个这样的区间可以使得序列中所有的数字相同。
思路分析
看下数据范围,暴力即可,枚举所有的数字即可。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 7; int a[maxn]; int main(){ int t; scanf("%d", &t); while (t --){ int n, k; scanf("%d%d", &n, &k); for (int i=1; i<=n; i++) scanf("%d", &a[i]); int ans = INF; for (int i=1; i<=100; i++){ int res = 0; for (int j=1; j<=n;){ if (a[j] == i) j ++; else { j += k; res ++; } } ans = min(ans, res); } printf("%d\n", ans); } return 0; }
C. Bouncing Ball
给出一个长度为n的01序列,0表示该位置没有平台,1表示该位置有平台。让一个球在第p个位置开始弹跳,每次弹跳的步长为k。有两种操作,我们可以花x代价给某个位置添加一个平台,也可以花y代价给去掉此时的第一个位置(即只能从头删除),且位置的个数不能少于p,即小球最少要弹跳一次。问要小球最终能够弹出序列的最少代价。
思路分析
dp。
对于一个位置而言,要么小球是从前面弹过来的,要么就是这个位置是小球的第一个落地点。所以对于‘0’而言,计算这个位置的代价是需要先将其补上,再算代价。
需要注意的是,在 (1, p-1) 这个区间内的位置不能当做第一个落地点,在 (p, p+k-1) 这个区间内的位置不能有小球从前方弹跳而来。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 7; int dp[maxn], a[maxn]; int main(){ int t; scanf("%d", &t); while (t --){ int n, p, k; scanf("%d%d%d", &n, &p, &k); for (int i=1; i<=n; i++) scanf("%1d", &a[i]); int x, y; scanf("%d%d", &x, &y); for (int i=p; i<=n; i++){ if (a[i] == 0){ dp[i] += x; if (i - k < p) dp[i] += (i-p) * y; else dp[i] = min(dp[i] + (i-p) * y, dp[i-k] + dp[i]); }else if (a[i] == 1){ if (i - k < p) dp[i] += (i-p) * y; else dp[i] = min(dp[i] + (i-p) * y, dp[i-k] + dp[i]); } } int ans = INF; for (int i=max(n-k+1, p); i<=n; i++) ans = min(ans, dp[i]); // for (int i=1; i<=n; i++) cout << dp[i] << " "; // cout << endl; printf("%d\n", ans); for (int i=1; i<=n; i++) dp[i] = 0; } return 0; }
D. XOR-gun
给一个长度为n的非递减的序列。可以进行这样的操作:每次取两个相邻的数字进行异或操作,并将其放回去。问最少需要多少步操作能够打破原序列的非递减性。当不能做到是输出-1。
思路分析
首先判断连续的三个是否能打破原序列的非递减性,如果能就直接输出1。不然就暴力每个区间去查找情况(类似于区间dp)。
至于能够暴力的原因,是当如果不存在连续的三个元素能够打破原序列的非递减性的时候,说明不存在三个连续的元素,其转化为二进制的时候最高位相同。而根据题目给出的范围可以知道,这样的序列个数很少。
#include <bits/stdc++.h> #define INF 0x3f3f3f3f using namespace std; const int maxn = 1e5 + 7; int a[maxn]; int p[maxn]; int main(){ int n; scanf("%d", &n); for (int i=1; i<=n; i++) scanf("%d", &a[i]); bool flag = false; for (int i=2; i<n; i++){ if ((a[i] ^ a[i-1]) > a[i+1] || (a[i] ^ a[i+1]) < a[i-1]){ flag = true; break; } } if (flag) printf("1\n"); else { for (int i=1; i<=n; i++) p[i] = a[i] ^ p[i-1]; int res = INF; for (int i=1; i<=n-1; i++){ for (int j=i; j<=n; j++){ for (int k=i; k<j; k++) if ((p[k] ^ p[i-1]) > (p[j] ^ p[k])) { res = min(res, j-i-1); // cout << "!!!! "; // cout << i << " " << j << endl; // cout << (p[k] ^ p[i-1]) << " " << (p[j] ^ p[k]) << endl; } } } if (res == INF) printf("-1\n"); else printf("%d\n", res); } return 0; }
E. New Game Plus!
有n个boss,每次打败一个boss,打败下一个boss的赏金就多ci,ci能是负数,同时有k次将boss赏金变为0的机会。问在随意选择boss且必须打败所有的boss的情况下,最多能获得的赏金是多少?(可能为负值)。
思路分析
贪心+思维。
首先可以想打的是,肯定先打高赏金的boss,这样在每一次赏金往后堆叠的时候,能够获得更高的赏金。而对于出现负数赏金的时候,就需要使用特殊机会。而在手动模拟的时候,可以发现k次机会将最终的负数分成了k+1个区间,区间内的数都按照各自在区间内的位置算贡献。这里可以利用栈来解释,每个数的贡献都是其距离栈底的距离乘上自己的数值,为贡献。(栈底表示栈最下面的数字)。所以可以想到的是,按照从小到大的顺序,将这些数字放入k+1个栈,以满足越小的数字离栈底越近。最终依次算出最终贡献加入答案即可。
#include <bits/stdc++.h> #define int long long using namespace std; const int maxn = 5e5 + 7; int a[maxn]; vector <int> v; signed main(){ int n, k; scanf("%lld%lld", &n, &k); for (int i=1; i<=n; i++) scanf("%lld", &a[i]); sort(a+1, a+1+n); reverse(a+1, a+1+n); // for (int i=1; i<=n; i++) cout << a[i] << " "; // cout << endl; int ans = 0, res = 0; for (int i=1; i<=n; i++){ ans += res; // cout << res << " "; res += a[i]; if (res < 0){ v.emplace_back(res); for (int j=i+1; j<=n; j++){ v.emplace_back(a[j]); } break; } } // cout << endl; reverse(v.begin(), v.end()); int ln = (int)v.size(); // for (int i=0; i<ln; i++) cout << v[i] << " "; // cout << endl; // cout << ans << endl; res = 0; for (int i=0; i<ln; i++) ans += v[i] * (i / (k + 1)); printf("%lld\n", ans); return 0; }