Codeforces Round #616 (Div. 2)

本文深入解析了Codeforces竞赛中的三道题目,包括如何通过删除数位使数变为奇数且位数和为偶数,如何判断数列能否形成金字塔形,以及在多人取数游戏中如何确保自己的收益最大化。文章提供了详细的解题思路与代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Codeforces Round #616 (Div. 2)

A. Even But Not Even

题意

输入一个位数不超过3000的数,删除其中的若干位,把它变成一个自身为奇数,所有位上的数之和为偶数的数。

题解

用字符串把数存起来,先找到最后一个为奇数的位置,将起后面的全部删掉,然后把剩下的数加起来,如果得到奇数,就在字符串中找一位奇数将其删掉,最后处理前导零。

代码

#include<iostream>
#include<cstdio>
using namespace std;
 
int main()
{   long N,i,n;
    long long sum;
    string s;
    cin>>N;
    while(N--){
        cin>>n>>s;
        while((s[s.length()-1]-'0')%2==0&&s.length()>1)
            s.erase(s.length()-1);
        sum=0;
        for(i=0;i<s.length();i++)
            sum+=s[i]-'0';
        if(sum%2)
        for(i=0;i<s.length()-1;i++)
            if((s[i]-'0')%2==1){
                s.erase(i,1);
                break;
            }
        while(s[0]=='0')
            s.erase(0,1);
        sum=0;
        for(i=0;i<s.length();i++)
            sum+=s[i]-'0';
        if((s[s.length()-1]-'0')%2&&sum%2==0)
            cout<<s<<endl;
        else
            cout<<-1<<endl;
    }
    return 0;
}

B. Array Sharpening

题意

有n个数形成的数列,可以对其中的所有数进行减法操作,问是否做到将其变成金字塔形。

题解

考虑到只能进行减法操作,故要想办法将所有数的需求最小化,容易证明到,在需求最小化后,最大点一定出现在中间位置。下面分两种情况:
1)当n为奇数时
在这里插入图片描述
最大点必然出现在n/2+1的位置上,故只要从头尾分别向中间循环判断即可
2)当n为偶数时
在这里插入图片描述
因为无法严格按照金字塔分布,所以有两种可能:
要么左边向右上多延伸半格,此时最大值出现在n/2的位置,
要么右边向左上多延伸半格,此时出现在n/2+1的位置。
故两端分别向中间判断,最后单独判断中间两个即可

代码

#include<cstdio>
#define def 300010
 
long a[def];
 
int main()
{   long N,n,i;
    bool t;
    scanf("%ld",&N);
    while(N--){
        scanf("%ld",&n);
        t=true;
        for(i=1;i<=n;i++)
            scanf("%ld",&a[i]);
        for(i=1;i<=n/2;i++)
            if(a[i]<i-1)
                t=false;
        for(i=n/2+1;i<=n;i++)
            if(a[i]<n-i)
                t=false;
        if(n%2==0)
            t&=(a[n/2]>=n/2||a[n/2+1]>=n/2);
        if(t)
            printf("Yes\n");
        else
            printf("No\n");
    }
    return 0;
}

C. Mind Control

题意

m个人在n个数里取数,每人可以在数列的开头或结尾取一个,自己是最后一个取数的,在所有人开始前,你可以强迫k个人一定取开头的或结尾的,每个人强迫的内容可以不同,问可以保证自己至少取多少

题解

先用最基本的搜索思想,无论是否强迫,n个数里被取掉的m-1个数一定在首尾在这里插入图片描述
如当m=4,k=0时,被取走的可能有a4,a5,a6或a1,a5,a6或a1,a2,a6或a1,a2,a3
被取走之后,我们可以任意取走剩下的首尾中较大的数,即max(a1,a3),max(a2,a4),max(a3,a4),max(a4,a6)
因为问的是至少,所以在这四个里面取得的最小值就是答案

但是现在加入了k值,这k个人的取法是我们可以控制的,由于需要确定性的答案,所以这k个人一定会先取
还是n=6,m=4,现在增加条件k=2
我们可以强迫取走a5,a6或a1,a6或a1,a2
然后在剩下的4个数中,又被随机取走m-1-k=1个数
取走后剩下的n-(m-1)=3个数中,首尾最大的就是当前的局部最优,记作ans1
由上面k=0的情况可以推出,强迫取走k个后的局部最优是min{ans1},记作ans2
因为k个可控,那么肯定要选最优取法,所以全局最优是max{ans2}

综上,可以用两个区间来加以限制
第一个是取走k个后的区间,这个区间的最大值就是全局最优
第二个是再取走m-1-k个后的区间,这个区间的最小值就是局部最优
剩下的区间首尾的最大值就是最优

代码

#include<cstdio>
#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#define def 4000
#define INF ~(1<<31)
 
long a[def];
 
int main()
{   long N,n,m,k,i,j,minn,ans=0;
    scanf("%ld",&N);
    while(N--){
        scanf("%ld%ld%ld",&n,&m,&k);
        ans=0;
        for(i=1;i<=n;i++)
            scanf("%ld",&a[i]);
        if(k>=m)
            for(i=1;i<=m;i++)
                ans=max(ans,max(a[i],a[i+n-m]));
        else for(i=1;i<=k+1;i++){
            minn=INF;
            for(j=0;j<=m-k-1;j++)
                minn=min(minn,max(a[i+j],a[i+n-m+j]));
            ans=max(ans,minn);
        }
        printf("%ld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值