题目来源
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;
}