• Codeforces Round #564 (Div. 2)


    传送门

    参考资料

      [1]: the Chinese Editoria

    A. Nauuo and Votes

    •题意

      x个人投赞同票,y人投反对票,z人不确定;

      这 z 个人由你来决定是投赞同票还是反对票;

      判断 x 与 y 的相对大小是否确定?

    题解

      如果 x == y && z == 0,输出 '0';

      如果 x-y > z,输出 '+';

      如果 y-x > z,输出 '-';

      反之,输出 '?';

    Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INF 0x3f3f3f3f
     4 #define ll long long
     5 #define mem(a,b) memset(a,b,sizeof(a))
     6 #define memF(a,b,n) for(int i=0;i <= n;a[i++]=b);
     7 const int maxn=1e3+50;
     8 
     9 int x,y,z;
    10 
    11 char *Solve()
    12 {
    13     if(x == y && z == 0)
    14         return "0";
    15     if(x-y > z)
    16         return "+";
    17     if(y-x > z)
    18         return "-";
    19     return "?";
    20 }
    21 int main()
    22 {
    23 //    freopen("C:\Users\hyacinthLJP\Desktop\in&&out\contest","r",stdin);
    24     scanf("%d%d%d",&x,&y,&z);
    25     puts(Solve());
    26 
    27     return 0;
    28 }
    View Code

    B.Nauuo and Chess(构造)

    题意

      给你 n 个棋子,求满足 "for all pairs of pieces i and j, |rirj|+|cicj≥ |i−j|."的最小的方形棋盘的列;

      方形棋盘的右下角(m,m)与左上角(1,1)的距离为 2×(m-1);

    题解

      ①找到 2×(m-1) ≥ n-1 的最小的 m;

      ②将 1~n-1 个棋子从第一行开始填充,第一行填充完,填充最后一列;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define memF(a,b,n) for(int i=0;i <= n;a[i++]=b);
     4 #define INF 0x3f3f3f3f
     5 const int maxn=2e5+50;
     6 
     7 int n;
     8 
     9 void Solve()
    10 {
    11     int m=(n+2)/2;
    12     printf("%d
    ",m);
    13     int x=1,y=1;
    14     for(int i=1;i < n;++i)
    15     {
    16         printf("%d %d
    ",x,y);
    17         if(y < m)///先填充第一行
    18             y++;
    19         else
    20             x++;
    21     }
    22     printf("%d %d
    ",m,m);
    23 }
    24 int main()
    25 {
    26     scanf("%d",&n);
    27     Solve();
    28 
    29     return 0;
    30 }
    View Code

    C. Nauuo and Cards(贪心)

    题意

      有 2n 张牌,其中 n 张标号 1~n,其余 n 中为空牌;

      从这 2n 张牌中拿出 n 张放在手中,剩余的 n 张摞在桌子上(牌堆);

      你可以进行如下操作:

        将手中的任意一张牌插入到牌堆的底部,并将牌堆顶端的牌放入手中;

      求最少的操作,使得摞在桌子上的 n 张牌从顶端到底端为 1,2,....,n;

    题解

      

      第一句话很好理解,主要是 “答案为 max(...)”这句话不太好理解,下面说说我的进一步理解:

      最终结果就是 1~n 摞在桌子上,并且有序;

      那么,存在牌k,在经过操作后,使得[1,2,...,k]在牌堆的底端,并且接下来的操作只用标号为[k+1,....,n]的牌,将其依次插入牌堆的底端;

      那么,这种情况下,答案就为 p[k]+1+n-k;

      其中 p[k]+1 是将牌 k 插入牌堆底端需要的最小操作,n-k是将[k+1,....,n]依次插入牌堆的最小操作;

    Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define INF 0x3f3f3f3f
     4 const int maxn=2e5+50;
     5 
     6 int n;
     7 int a[maxn];
     8 int b[maxn];
     9 int p[maxn];///p[i]:第i张牌在b中的位置,如果在a中,那么p[i]=0;
    10 
    11 int F()///判断是否可以只将标号牌插入牌堆的底端使得最终状态满足条件
    12 {
    13     if(!p[1])
    14         return INF;
    15     int cur=2;
    16     for(;p[cur] == p[1]+cur-1;cur++);
    17     if(p[cur-1] == n)///[1,2,...,cur-1]在b中最后的位置
    18     {
    19         int k=cur;
    20         /**
    21             在依次将第k(k∈[cur,n])张牌插入底部的时候要确保k在手上
    22             如何确保k在手上呢?
    23             假设[cur,k-1]成功插入到底部;
    24             那么,这k-cur牌的插入势必会使得b中的前k-cur张牌拿到手中;
    25             那么,只要b中前k-cur张牌含有k就行;
    26             也就是p[k]<=k-cur;
    27         */
    28         for(;k <= n && p[k] <= k-cur;k++);
    29         if(k > n)///如果k=n+1,那么只操作[cur,n]便可满足条件
    30             return n-(cur-1);
    31     }
    32     return INF;
    33 }
    34 int G()
    35 {
    36     /**
    37         ①如果[1,..,i]中标号为x的牌,x在b中的位置在i之后
    38          也就是说 x<i,p[x]>p[i];
    39          那么 p[x]+1+n-x > p[i]+1+n-i;
    40          对于这种情况,ans只会取x对应的操作次数
    41         ②如果经过p[i]+1次操作后使得[1,...i]插入牌堆的底端
    42          但是,i之后存在标号为x,y的牌,x<y,p[x]>p[y]
    43          那么,[i+1,...,n]是没法依次插入牌堆的
    44          但,答案会是p[i]+1+n-i吗?
    45          易得p[x] >= p[i]+2
    46          第i张牌的操作次数为 p[i]+1+n-i;
    47          第x张牌的操作次数为 p[x]+1+n-x;
    48          两者做差得 p[x]+1+n-x-(p[i]+1+n-i)=p[x]-p[i]+i > 0
    49          所以,ans只会取x对应的操作次数,而不会取i对应的操作次数
    50     */
    51     int ans=0;
    52     for(int i=1;i <= n;++i)
    53         ans=max(ans,p[i]+1+n-i);
    54     return ans;
    55 }
    56 int main()
    57 {
    58     scanf("%d",&n);
    59     for(int i=1;i <= n;++i)
    60     {
    61         scanf("%d",a+i);
    62         p[a[i]]=0;
    63     }
    64     for(int i=1;i <= n;++i)
    65     {
    66         scanf("%d",b+i);
    67         p[b[i]]=i;
    68     }
    69     printf("%d
    ",min(F(),G()));
    70     
    71     return 0;
    72 }
    View Code

    •二次理解2019.10.21

      因为明天要帮老师讲有关贪心的实验课,今天就将之前做的贪心的题复习了一下;

      对这道题有了新的理解;

      将所有操作分为两类:

        (1)牌面为 1 的牌从手中打到桌子上

        (2)牌面为 1 的牌在桌子上,并且在不打空白牌的情况下就可以完成

      首先判断(2)是否满足,如果满足,那此时的解肯定是最优解;

      如果不满足(2),如何快速求解(1)对应的最优解呢?

      定义 $f_i$ 表示将牌放入手中所需的最小操作;

      因为要使得桌子上的牌连续,所以,在打出 1 这张牌后,紧接着要打 2 这张牌,接着是 3,......

      也就是说,$f_2 leq f_1+1 , f_3 leq f_1+2 ,cdots , f_n leq f_1+n-1$;

      所以说,要在 $max_{i=1}^{n} (f_i-(i-1))$ 后打出牌 1 才能确保接下来打出的牌依次为 2,3,4,....,n;

      所以,(1)的最优解为 $max_{i=1}^{n} (f_i-(i-1)) + n$;

    •Code

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const int maxn=2e5+50;
     4 
     5 int n;
     6 int a[maxn];
     7 int b[maxn];
     8 int f[maxn];
     9 
    10 int Calc()
    11 {
    12     if(f[1] == 0)
    13         return -1;
    14     for(int i=f[1]+1;i <= n;++i)
    15         if(b[i] != b[i-1]+1)
    16             return -1;
    17 
    18     int ans=0;
    19     int k=0;
    20     for(int i=b[n]+1;i <= n;++i)
    21     {
    22         if(f[i] > k)
    23             return -1;
    24         ans++;
    25         k++;
    26     }
    27     return ans;
    28 }
    29 int Solve()
    30 {
    31     int ans=Calc();
    32     if(ans != -1)
    33         return ans;
    34 
    35     ans=f[1];
    36     for(int i=2;i <= n;++i)
    37         ans=max(ans,f[i]-(i-1));
    38     return ans+n;
    39 }
    40 int main()
    41 {
    42     scanf("%d",&n);
    43     for(int i=1;i <= n;++i)
    44     {
    45         scanf("%d",a+i);
    46         f[a[i]]=0;
    47     }
    48     for(int i=1;i <= n;++i)
    49     {
    50         scanf("%d",b+i);
    51         f[b[i]]=i;
    52     }
    53     printf("%d
    ",Solve());
    54 
    55     return 0;
    56 }
    View Code
  • 相关阅读:
    使用C实现一个函数内两个阿拉伯数字的交换
    使用C比较两个字符串是否相等
    C打印99乘法表
    C定时在控制台输出时分秒
    C实现猜数字
    OpenCV实现开操作、闭操作、形态学梯度、顶帽、黑帽
    OpenCV实现膨胀和腐蚀
    metaclass of python
    Django设计哲学
    using NGINX+uWSGI to deploy django project
  • 原文地址:https://www.cnblogs.com/violet-acmer/p/10989957.html
Copyright © 2020-2023  润新知