返回顶部

Codeforces Round #658 (Div. 2)【ABCD】(题解)

涵盖知识点:思维、dp

比赛链接:传送门

A - Common Subsequence

题意: 找串\(a,b\)的最短公共子序列
题解: 找一个相同的字母即可。若全不同则不存在。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1010;
int a[maxn],b[maxn];
int main(){
    int t;cin>>t;
    while(t--){
        int n,m;
        cin>>n>>m;
        memset(a,0,sizeof a);
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            a[x]=1;
        }
        int ans=0;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            if(a[x]==1){
                ans=x;
            }
        }
        if(ans){
            cout<<"YES\n"<<1<<" "<<ans<<"\n";
        }
        else cout<<"NO\n";
    }
    return 0;
}

B - Sequential Nim

题意: 博弈,每个人可以从含有石子的序号最小的堆中拿走任意正数个石子。
题解: 一般情况下先手必胜,可以每堆都拿到剩1个,然后最后一堆一次性拿完。如果中间存在1个的堆,那么可以一次性拿完前一堆,另一人只能拿完那一堆,回到正常的状态。但是如果开头存在奇数个1个的堆,那么先后手交换。特判一下所有堆都是1的情况。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int a[maxn];
int main(){
    int t;cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(a[i]==1)cnt++;
            else break;
        }
        if(cnt==n)cnt++;
        puts(cnt&1?"Second":"First");
    }
    return 0;
}

C2 - Prefix Flip (Hard Version)

题意: 给定两个二进制串\(a,b\)。每次选择一个数字,将\(a\)的前缀各位取反后再倒序。要求在\(2n\)次操作后将\(a\)变为\(b\)
题解: 先最多花费\(n\)次将\(a\)所有位变为相同数字,再从后往前确定每一位的数字。若对应位不同,则对其包含在内的所有前缀进行翻转。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int a[maxn];
int main(){
    int t;cin>>t;
    while(t--){
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(a[i]==1)cnt++;
            else break;
        }
        if(cnt==n)cnt++;
        puts(cnt&1?"Second":"First");
    }
    return 0;
}

D - Unmerge

题意: 给定\(2n\)长度的数组\(p\),问是否可能为两个长度为\(n\)\(a,b\)合并而成,合并规则大概是依次从\(a,b\)中第一个未被选用的位置中选择一个较大的数字加入\(p\)
题解: 观察\(p\),按照向右的第一个比当前段最大值大的值进行分段。以\([3,2,6,1,5,7,8,4]\)为例,将其分为\([3,2],[6,1,5],[7],[8,4]\)四段。可以很明显的看出,我们只有将这些段任意组合的结果才可以得到最后的串。所以我们只要判断这些段能否组成长度为\(n\)即可。dp一下就可以了。
Accept Code:

#include <bits/stdc++.h>
using namespace std;
const int maxn=4010;
vector<int> idx;
int dp[maxn],v[maxn];
int main(){
    int t;cin>>t;
    while(t--){
        idx.clear();
        memset(dp,0,sizeof dp);
        memset(v,0,sizeof v);
        int n;
        cin>>n;
        int mx=0;
        for(int i=0,a;i<2*n;i++){
            cin>>a;
            if(a>mx){
                mx=a;
                idx.push_back(i);
            }
        }
        for(int i=0;i<idx.size()-1;i++){
            v[i]=idx[i+1]-idx[i];
        }
        for(int i=0;i<idx.size();i++){
            for(int j=n;j>=v[i];j--){
                dp[j]=max(dp[j],dp[j-v[i]]+v[i]);
            }
        }
        puts(dp[n]==n?"YES":"NO");
    }
    return 0;
}
posted @ 2020-07-24 10:26  Charles1999  阅读(124)  评论(0编辑  收藏  举报