• Codeforces Round #658 (Div. 2)(BCD)


    题目链接:https://codeforces.com/contest/1382

    B. Sequential Nim(博弈)

    C. Prefix Flip (思维题)

    D. Unmerge(背包DP)

    B. Sequential Nim

    题目大意:给你n堆石子,每堆$a_i$个,你必须按顺序从左到右取,每次取至少一个,最多$a_i$个,问先手胜利还是后手。$nleq 10^5 a_ileq 10^9$

    Example
    input
    7
    3
    2 5 4
    8
    1 1 1 1 1 1 1 1
    6
    1 2 3 4 5 6
    6
    1 1 2 1 2 2
    1
    1000000000
    5
    1 2 2 1 1
    3
    1 1 1
    
    output
    First
    Second
    Second
    First
    First
    Second
    First

    emmm,我们先不考虑1的情况,那么先手在最后一堆石子前必然拿走$a_i-1$个石子,然后先手永远是先手,所以先手必胜。

    那么如果有一个1呢?我们考虑1出现的位置,有两种情况,1在最前面,这个时候,先手被迫拿走1,那么先手和后手的顺序就会被逆转,先手必败。如果1不在最前面,那么先手一定可以使得在1的前面拿走$a_i$个石子,使得后手被迫拿走1,那么先手还是先手,依然是必胜。

    那么接下来似乎就不需要考虑了,通过对1个1的观察我们就可以得出结论了,我们只需要考虑前缀1的个数就行了。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=1e5+10;
    
    int a[mac];
    
    int main(int argc, char const *argv[])
    {
        int t;
        scanf ("%d",&t);
        while (t--){
            int n;
            scanf ("%d",&n);
            for (int i=1; i<=n; i++)
                scanf ("%d",&a[i]);
            int f=1,s=0,cnt=0;
            for (int i=1; i<=n; i++){
                if (a[i]==1) cnt++;
                else break;
            }
            if (cnt&1){
                if (cnt==n) printf("First
    ");
                else printf("Second
    ");
            }
            else{
                if (cnt==n) printf("Second
    ");
                else printf("First
    ");
            }
        }
        return 0;
    }
    View Code

    C. Prefix Flip 

    题目大意:给你2个长度为$n$的01串,你需要将第一个01串通过变换变成第二个01串。你可以进行的变换为,选择一个前缀长度,然后将其元素翻转,然后再翻转其位置。比如说001001,选择长度为3的前缀,那么会得到011001。问你s1可以通过几步得到s2,并输出每次选择的前缀长度。$|S|leq 10^5$,步数必须在$2n$以内

    Example
    input
    5
    2
    01
    10
    5
    01011
    11100
    2
    01
    01
    10
    0110011011
    1000110100
    1
    0
    1
    
    output
    3 1 2 1
    6 5 2 5 3 1 2
    0
    9 4 1 2 10 4 1 2 1 5
    1 1

    此题最为麻烦的就是要考虑位置的翻转,这样翻转之后整个过程就变得非常不可控。所以我们想如何将这个位置翻转从考虑的因素中剔除掉。我们能想到,对于几个相同的元素进行翻转,他们的位置翻转是没有意义的。那么我们就可以考虑直接将串s1全部变成相同的元素,然后再将s1变为s2。这个过程所需要的步数一定是在$2n$以内的。

    我们从前往后跑,然后相邻的两个元素不一样,那么我们就将前面的元素全部翻转,那么就可以得到了该位置下前缀的所有元素都一样了。那么最多执行n-1次。

    接下来从后往前跑,如果s1和s2的该位置不一样,我们直接将该位置的前缀翻转就可以了。最多执行n次。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=1e5+10;
    const int inf=1e9+10;
    
    char a[mac],b[mac];
    
    int main(int argc, char const *argv[])
    {
        int t;
        scanf ("%d",&t);
        while (t--){
            int n;
            scanf ("%d",&n);
            scanf ("%s",a+1);
            scanf ("%s",b+1);
            vector<int>ans;
            for (int i=2; i<=n; i++){
                if (a[i]!=a[i-1]) ans.push_back(i-1);
            }
            char last=a[n];
            for (int i=n; i>=1; i--){
                if (b[i]!=last){
                    ans.push_back(i);
                    last=last=='0'?'1':'0';
                }
            }
            printf("%d ",ans.size());
            for (int i=0; i<ans.size(); i++)
                printf("%d ",ans[i]);
            printf("
    ");
    
        }
        return 0;
    }
    View Code

    D. Unmerge

    题目大意:有两个长度为n的集合,进行merge操作,每次取两个集合最前面的最小值,那么可以得到一个新的长度为2n的集合,现在给你这个长度为2n的集合,问你是否存在着2个长度为n的集合通过merge操作得到它。$n<leq 2000$

    Example
    input
    6
    2
    2 3 1 4
    2
    3 1 2 4
    4
    3 2 6 1 5 7 8 4
    3
    1 2 3 4 5 6
    4
    6 1 3 7 4 5 8 2
    6
    4 3 2 5 1 11 9 12 8 6 10 7
    
    output
    YES
    NO
    YES
    YES
    NO
    NO

    emmm,我们将这个序列进行分段,找到一个数后面第一个比他大的元素进行分段,比如2 3 1 4可以分为2| 3 1| 4。然后跑对每段的长度跑01背包就可以了,最后判断一下背包的价值是否为n。

    至于为什么这样分的。。。我们可以观察,对于两个集合(我们可以看做是一个栈),每次出来的都是栈顶最小的元素,那么如果先出来了个17,那么后面出来的元素,如果比17来得小,那么一定是和17同栈的,因为,如果不是同栈的话出来的就是将17踢掉的那个元素,它肯定会大于17的,所以我们可以这样来分段。

    以下是AC代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int mac=4e3+10;
    
    int len[mac],a[mac],dp[mac];
    
    int main(int argc, char const *argv[])
    {
        int t;
        scanf ("%d",&t);
        while (t--){
            int n;
            scanf ("%d",&n);
            for (int i=1; i<=2*n; i++)
                scanf ("%d",&a[i]);
            int cnt=1,mx=a[1],num=1;
            len[cnt]=1;
            for (int i=2; i<=2*n; i++){
                if (a[i]>mx) len[cnt++]=num,num=1,mx=a[i];
                else num++;
            }
            for (int i=0; i<=2*n; i++) dp[i]=0;
            for (int i=1; i<cnt; i++)
                for (int j=n; j>=len[i]; j--)
                    dp[j]=max(dp[j],dp[j-len[i]]+len[i]);
            if (dp[n]==n) printf("YES
    ");
            else printf("NO
    ");
        }
        return 0;
    }
    View Code
    路漫漫兮
  • 相关阅读:
    线性dp 打鼹鼠
    区间dp 能量项链 洛谷p1063
    洛谷 CF1012C Hills (动态规划)
    交作业了 动态规划 木棍加工
    最短路之Floyd
    最小生成树
    寒假集训并查集初级版
    【倍增DP】——保卫王国
    bootstrap四部分概述
    zrender初识
  • 原文地址:https://www.cnblogs.com/lonely-wind-/p/13361589.html
Copyright © 2020-2023  润新知