CCPC 2018桂林D G H J

桂林2018 CCPC 重现赛后补题 D G H J

在这里插入图片描述

题意:给定两个数A和B,是否能将A转化为B并操作次数最小,通过以下操作:将数的一个二进制位定住交换此二进制位的左右两个数,例如:110将第二位定住,交换第一位和第三位,使其变成011。如果最终A能变成B则输出最小的操作次数,否则输出-1。

思路:可以发现二进制形式下的数奇数位和偶数位是互不干扰的,那么如果A的二进制形式下奇数位1的个数不等于B的二进制形式下奇数位1的个数或者偶数位1的个数不相等那么A肯定无法转换成B的形式输出-1,否则的话我们需要找到最小操作次数,那么通过贪心的思想使得A通过最小操作次数转换成B。

参考代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);//加速器
const int N=70;
int a[N],b[N];
int solve(){
    
    
    ll x,y;
    cin>>x>>y;
    for(int i=0;i<70;i++){
    
    //将数二进制存储到a和b数组之中
        a[i]=x&1;
        b[i]=y&1;
        x>>=1;
        y>>=1;
    }
    vector<int>v1,v2;//第一次用来存放奇数位上1的下标,第二次用来存储偶数位上1的下标
    int res=0;
    for(int i=0;i<70;i+=2){
    
    
        if(a[i])v1.push_back(i);
        if(b[i])v2.push_back(i);
    }
    if(v1.size()!=v2.size())return -1;//如果个数不相等,则说明A无法变成B放回-1
    for(int i=0;i<v1.size();i++){
    
    
        res+=abs(v1[i]-v2[i])/2;//贪心思想,既然奇数位上的1的个数相同,则将所有A中1的位置对标B中同样次序1的下标即可
    }//每次都是翻转下标的偏移量是2所以还要除以2
    v1.clear(),v2.clear();//clear,然后记录偶数位,进行同样操作
    for(int i=1;i<70;i+=2){
    
    
        if(a[i])v1.push_back(i);
        if(b[i])v2.push_back(i);
    }
    if(v1.size()!=v2.size())return -1;
    for(int i=0;i<v1.size();i++){
    
    
        res+=abs(v1[i]-v2[i])/2;
    }
    return res;//最后将奇数位和偶数位操作次数相加就是最小的操作次数
}
int main(){
    
    
    IOS;
    int T;
    cin>>T;
    int cas=0;
    while(T--){
    
    
        printf("Case %d: %d\n",++cas,solve());
    }
    return 0;
}

G. Greatest Common Divisor
在这里插入图片描述

题意:每次操作可以将数组中的每个数加1,求最少的操作次数使得数组中所有数的gcd>1,如果无法使得所有数gcd>1则返回-1

思路:如果最后的gcd>1,那么n个数每个数都是gcd的倍数,那么他们的差值也是gcd的倍数,所以求所有差值(排序后)的gcd,如果gcd == 1,则没有办法使他们的gcd>1,但是需要特判下如果所有的数都相等而且>=2,那么0次就够了,如果gcd ==1的话则需要都加1.对于其他情况的话,暴力求出来gcd的因子,然后对于随便一个数验证次数,取一个最小值即可。

参考代码

#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);//加速器
const int N=1e5+10;
int a[N];
int solve(){
    
    
    int n;
    cin>>n;
    unordered_map<int,int>S;//哈希存储
    for(int i=1;i<=n;i++){
    
    cin>>a[i];S[a[i]]++;}
    sort(a+1,a+1+n);
    if(n==1){
    
    //如果只有一个数,那么是1操作一次否则不用操作
        if(a[1]==1)return 1;
        else return 0;
    }
    int gd=a[2]-a[1];
    for(int i=3;i<=n;i++){
    
    
        gd=gcd(gd,a[i]-a[i-1]);
    }
    if(gd==1){
    
    //如果gd==1判断是不是全是1或者全是>1的同一个数
        if(a[1]==1&&S[a[1]]==n)return 1;
        else if(S[a[1]]==n)return 0;
        else return -1;
    }
    if(gcd(gd,a[1])>1)return 0;//如果a[1]已经包含了gcd那么不用操作直接返回即可
    for(int i=2;i<=1e5+10;i++){
    
    //暴力找gcd中的最小因子
        if(gd%i==0){
    
    
            gd=i;
            break;
        }
    }
    return gd-a[1]%gd;//最小操作数
}
int main(){
    
    
    IOS;
    int T;
    cin>>T;
    int cas=0;
    while(T--){
    
    
        printf("Case %d: %d\n",++cas,solve());
    }
    return 0;
}

在这里插入图片描述

题意:构造一个字典序最小的字符串同时满足长度与串AB相等,并且与A的汉明距离等于与B的汉明距离

思路:最贪的构造无非是所有的字符全是’a’,但是因为要满足与A的汉明距离等于与B的汉明距离,那么我们也要尽可能保证前面出现尽可能多的’a’。

参考代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int f[N];
string a,b;
int nowdis;
bool check(char x,int idx){
    
    
    int now=nowdis;
    if(a[idx]!=x)now++;
    if(b[idx]!=x)now--;
    return abs(now)<=f[idx+1];
}
void solve(){
    
    
    cin>>a>>b;
    f[a.size()]=0;
    for(int i=a.size()-1;i>=0;i--){
    
    //用f记录汉明距离
        f[i]=f[i+1];
        if(a[i]!=b[i])f[i]+=1;
    }
    nowdis=0;
    for(int i=0;i<a.size();i++){
    
    //因为尽可能的使前面尽可能多的a所以从前面开始
        for(char j='a';j<='z';j++){
    
    
            if(!check(j,i))continue;//如果当前check不满足,也就是使用当前字符不满足下一位的汉明距离,那么当前字符跳过
            cout<<j;
            if(a[i]!=j)nowdis++;//如果可以使用当前的字符那么更新nowdis
            if(b[i]!=j)nowdis--;
            break;
        }
    }
    cout<<endl;
    return ;
}
int main(){
    
    
    int t;
    cin>>t;
    int cas=0;
    while(t--){
    
    
        printf("Case %d: ",++cas);
        solve();
    }
    return 0;
}

在这里插入图片描述
在这里插入图片描述

题意:有很多堆石头,从Alice开始拿,每次拿一颗并且保证拿完这颗石头之后,这堆石头的剩余石头不能等于其相邻堆的石头数量,两人谁先无法继续拿石头判定为负,另一方胜利

扫描二维码关注公众号,回复: 13443361 查看本文章

思路:每次都从剩余中没拿过石头的最小的入手,如果当前操作堆小于左右两堆那么可以使其变成0,否则的话最多只能变成max(a[L],a[R])+1。这也是为什么每次从目前未操作的最小堆入手由来,因为如果出现比当前操作堆小的左堆或者右堆那么一定是之前已经更新了的最小数量(指左堆或右堆小于当前操作堆)。那么其实也有了贪心的思想存在,最后将所有的操作数相加如果&1那么A WIN 否则 B WIN。

参考代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);//加速器
const int N=1e5+10;
pair<int,int>s[N];
int a[N];
void solve(){
    
    
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
    
    
        cin>>s[i].first;
        s[i].second=i;
        a[i]=s[i].first;
    }
    a[0]=-1;//处理一下边界,因为边界不影响a[1]或者a[n]拿到尽量最小
    a[n+1]=-1;
    sort(s+1,s+1+n);
    int res=0;//记录操作数
    for(int i=1;i<=n;i++){
    
    
        int idx=s[i].second;
        int MI=-1;
        if(a[idx]>a[idx-1])MI=max(MI,a[idx-1]);
        if(a[idx]>a[idx+1])MI=max(MI,a[idx+1]);
        MI++;
        res+=a[idx]-MI;
        a[idx]=MI;
    }
    if(res&1)puts("Alice");
    else puts("Bob");
    return ;
}
int main(){
    
    
    IOS;//血的教训,IOS一定要放在所有输入输入操作前面预处理
    int T;
    cin>>T;
    int cas=0;
    while(T--){
    
    
        printf("Case %d: ",++cas);
        solve();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_53504307/article/details/120636774
今日推荐