Codeforces Round #658 (Div. 2) A-D题解

题目 A B C1 C2 D E

rank 503/24068

A - Common Subsequence

需要求的是最短公共子序列,一开始没反应过来卡了一会。

只有两种情况:两个串有相同字符和没有相同字符。

#include 
 
using namespace std;
typedef long long LL;
 
int b[1005];
bool have[1005];
 
int main() {
    int t = 0;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        for (int i = 0; i < 1005; ++i) {
            have[i] = false;
        }
        for (int i = 0; i < n; ++i) {
            scanf("%d", a + i);
            have[a[i]]  = true;
        }
        for (int j = 0; j < m; ++j) {
            scanf("%d", b + j);
        }
        bool ok = false;
        for (int i = 0; i < m; ++i) {
            if(have[b[i]]) {
                if(!ok)cout<<"YES"<<endl;
                cout<<1<<' '<<b[i]<<endl;
                ok = true;
                break;
            }
        }
        if(!ok)cout<<"NO"<<endl;
    }
}

B - Sequential Nim

从后向前推可以找到必胜者。

当前值为1 -> 必胜者对换

当前值不为1 -> 第一个取该值的人胜利

#include 
 
using namespace std;
typedef long long LL;
 
int a[100005];
 
int main() {
 
    int t = 0;
    cin >> t;
    while (t--) {
        int n, m;
        cin >> n ;
        int win = 0;
        for (int i = 0; i < n; ++i) {
            scanf("%d", a + i);
        }
        bool nxtwin = false;
        for (int j = n - 1; j >= 0; --j) {
            if(j == n - 1) {
                win = 1;
                nxtwin = true;
            }
            else if(a[j] > 1) {
                if(!nxtwin){
                    nxtwin = true;
                    win++;
                }
            }
            else {
                win ++;
                nxtwin = !nxtwin;
            }
        }
        if(win&1)cout<<"First\n";
        else cout<<"Second\n";
    }
}

C1 - Prefix Flip (Easy Version)

C2 - Prefix Flip (Hard Version)

对第一个进行翻转取反、前n个进行翻转取反,可以使a的第n个字符与b相同,从后向前进行操作,最多进行2n步。

每次操作时需要维护当前a串第一个字符在原a串中的位置和取反次数。

复杂度O(n)。

#include 

using namespace std;
typedef long long LL;

string a, b;

int ai, aj;
bool rev;//剩余序列是否正序
bool xx;//是否翻转

void flip(int p){
    if(rev) aj--;
    else ai++;
    rev = !rev;
    xx = !xx;
}
char get(){
    int c = (rev? a[aj] : a[ai]) - '0';
    if(xx) c = !c;
    return '0' + c;
}


int main() {

    int t = 0;
    cin >> t;
    while (t--) {
        int n;
        cin>>n;
        cin>>a>>b;
        ai = 0;
        aj = n - 1;
        rev = false;
        xx = true;
        vector<int> ans;
        for (int i = n - 1; i >= 0; i--) {
            if(get() != b[i]) ans.push_back(1);
            ans.push_back(i+1);
            flip(n-1);
        }
        cout<<ans.size()<<' ';
        for (int j : ans) {
            cout<<j<<' ';
        }
        cout<<endl;
    }
}

D - Unmerge

可以发现对于合并后的连续递减序列,必在合并前的同一个序列中。

因此可以将合并后的序列划分为一些连续递减片段,判断使用这些片段是否可以填满容量为n的背包。

#include 

using namespace std;
typedef long long LL;

int main() {
    int t = 0;
    cin >> t;
    while (t--) {
        int n;
        int p[2005*2];
        cin>>n;
        for (int i = 0; i < 2 * n; ++i) {
            scanf("%d", p+i);
        }
        vector<int> pieces = {};
        for (int i = 0; i < 2*n;) {
            int j = i + 1;
            while(j < 2 * n && p[i] > p[j]){
                j++;
            }
            pieces.push_back(j - i);
            i = j;
        }
        int dp[2005][2] = {};
        bool tt = 0;
        for (int p : pieces) {
            for (int i = 1; i <= n; ++i) {
                if(i < p)dp[i][tt] = dp[i][!tt];
                else dp[i][tt] = max(dp[i-p][!tt] + p, dp[i][!tt]);
            }
            tt = !tt;
        }
        cout<<(dp[n][tt] == n? "YES":"NO")<<endl;
    }
}

你可能感兴趣的:(acm练习)