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
View Code1 #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 }
B.Nauuo and Chess(构造)
•题意
给你 n 个棋子,求满足 "for all pairs of pieces i and j, |ri−rj|+|ci−cj| ≥ |i−j|."的最小的方形棋盘的列;
方形棋盘的右下角(m,m)与左上角(1,1)的距离为 2×(m-1);
•题解
①找到 2×(m-1) ≥ n-1 的最小的 m;
②将 1~n-1 个棋子从第一行开始填充,第一行填充完,填充最后一列;
•Code
View Code1 #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\n",m); 13 int x=1,y=1; 14 for(int i=1;i < n;++i) 15 { 16 printf("%d %d\n",x,y); 17 if(y < m)///先填充第一行 18 y++; 19 else 20 x++; 21 } 22 printf("%d %d\n",m,m); 23 } 24 int main() 25 { 26 scanf("%d",&n); 27 Solve(); 28 29 return 0; 30 }
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
View Code1 #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\n",min(F(),G())); 70 71 return 0; 72 }
•二次理解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
View Code1 #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\n",Solve()); 54 55 return 0; 56 }