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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值