Educational Codeforces Round 82 (Rated for Div. 2)
A. Erasing Zeroes
题意:给定01组成的字符串,问最少删除几个0使得所有1连续。
思路:容易发现除了前缀的0和后缀的0不用删除以外,1内部的0都要删除。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int inf=105; 17 using namespace std; 18 int T,n; 19 char s[inf]; 20 int main(){ 21 // freopen("in.txt","r",stdin); 22 rd(T); 23 while(T--){ 24 scanf("%s",s);n=strlen(s); 25 int cnt=0;bool flg=0; 26 for(int i=0;i<n;i++){ 27 if(s[i] == '1'){flg=1;break;} 28 cnt--; 29 } 30 if(flg)for(int i=n-1;i>=0;i--){ 31 if(s[i] == '1')break; 32 cnt--; 33 } 34 for(int i=0;i<n;i++) 35 if(s[i] == '0')cnt++; 36 printf("%d\n",cnt); 37 } 38 return 0; 39 } 40 /**/
B. National Project
题意:需要花费n天修铁路,每天你可以选择修或者不修,从第一天开始,g天好天,b天坏天,修完铁路时必须保证至少有一半路程是在好天修的,问最少需要多少天。
思路:如果g>=b,那么每天都修即可,需要n天。如果g<b,把g个好天和b个坏天看成一组,完成任务时一定是每一个好天都在干活并且在最后一个好天所在组结束之前一定可以完工,需要u=(n+1)/2个好天,(u+g-1)/g-1即可算出完成任务时有几个整组,加上余下需要的好天即可,但需要注意当前不一定修完了,例如g=4,b=5,n=15,一定要用最后一组内剩余的好天或者坏天补齐n天(一定补的齐),其实就是对上面的答案和n取max。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 using namespace std; 17 int T; 18 int n,g,b; 19 int main(){ 20 // freopen("in.txt","r",stdin); 21 rd(T); 22 while(T--){ 23 rd(n);rd(g);rd(b); 24 if(g >= b)printf("%d\n",n); 25 else { 26 int u=(n+1)/2; 27 int v=(u+g-1)/g-1; 28 LL ans=1ll*v*(g+b)+u-v*g; 29 if(ans < n)ans=n; 30 printf("%lld\n",ans); 31 } 32 } 33 return 0; 34 } 35 /**/
反思:记住(u+g-1)/g-1这种计算方式,效果是u/g下取整但是在u%g==0时答案-1。
C. Perfect Keyboard
题意:给一个序列,问是否存在一个26字母的排列使得可以在每次只能选择当前字母旁边的字母输出的情况下打出这个序列。
思路:考虑贪心,相邻两个字母连一条边,如果原本两者之间的距离不是1,那么就不合法,如果一个字母和三个字母连了边,那么就不合法,最终会变成一条链和一堆点的集合,根据入度输出答案即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int inf=205; 17 using namespace std; 18 int T; 19 char s[inf]; 20 int a[26][26],n; 21 int dis[26]; 22 queue<int>S; 23 int get_dis(int x,int y){ 24 memset(dis,0,sizeof(dis)); 25 while(!S.empty())S.pop();S.push(x); 26 while(!S.empty()){ 27 int u=S.front();S.pop(); 28 if(u == y)break; 29 for(int i=0;i<26;i++) 30 if(a[u][i] && !dis[i]){ 31 S.push(i);dis[i]=dis[u]+1; 32 } 33 } 34 return dis[y]; 35 } 36 bool vis[26]; 37 void out(int x){ 38 printf("%c",x+'a');vis[x]=1; 39 for(int i=0;i<26;i++)if(a[x][i] && !vis[i])out(i); 40 } 41 int in[26]; 42 int main(){ 43 // freopen("in.txt","r",stdin); 44 rd(T); 45 while(T--){ 46 scanf("%s",s);n=strlen(s); 47 memset(a,0,sizeof(a)); 48 memset(in,0,sizeof(in)); 49 bool flg=0; 50 for(int i=0;i<n-1;i++){ 51 int u=s[i]-'a',v=s[i+1]-'a'; 52 if(get_dis(u,v) > 1){ 53 flg=1;break; 54 } 55 if(!a[u][v])in[u]++,in[v]++; 56 a[u][v] = a[v][u]=1; 57 } 58 for(int i=0;i<26;i++)if(in[i] >= 3)flg=1; 59 if(flg)printf("NO\n"); 60 else { 61 printf("YES\n");memset(vis,0,sizeof(vis)); 62 for(int i=0;i<26;i++)if(!vis[i] && in[i]<=1)out(i);puts(""); 63 } 64 } 65 return 0; 66 } 67 /**/
D. Fill The Bag
题意:给一个n,以及序列an,ai均为2^x,一次操作可以把ai拆成相等的两半,问最少几次操作可以使得一部分ai之和为n。
思路:如果所有ai之和大于等于n,那么一定存在一种解决方案,因为可以全部分解为1。从小到大枚举n二进制下的每一位,如果为1且存在对应ai,那么用掉这个ai并将多余的用于"拼接"成更高位,如果没有ai,则将离其最近的一个高位拆解,并且从这一位到被拆解的高位之间的位都不需要再计算了,如果为0则直接"拼接"为更高位。
代码是考场写的,思路很混乱,有些地方不优美简洁。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int inf=1e5+10; 17 using namespace std; 18 int T; 19 LL n; 20 int m,a[inf]; 21 int cnt[100]; 22 map<int,int>S; 23 int sta[100],top; 24 int main(){ 25 // freopen("in.txt","r",stdin); 26 rd(T);int now=1; 27 for(int i=0;i<=30;i++)S[now]=i,now*=2; 28 while(T--){ 29 lrd(n);rd(m);memset(cnt,0,sizeof(cnt));LL sum=0; 30 for(int i=1;i<=m;i++)rd(a[i]),cnt[S[a[i]]]++,sum+=a[i]; 31 if(sum < n)printf("-1\n"); 32 else { 33 int now=0;top=0; 34 while(n){ 35 if(n & 1){ 36 if(cnt[now])cnt[now]--; 37 else sta[++top]=now; 38 } 39 now++;n/=2; 40 } 41 int ans=0; 42 for(int i=1;i<=top;){ 43 for(int j=0;j<sta[i];j++)cnt[j+1]+=cnt[j]/2,cnt[j]%=2; 44 for(int j=sta[i];j<=99;j++){ 45 if(cnt[j]){ 46 ans+=j-sta[i];cnt[j]--;for(int k=sta[i];k<j;k++)cnt[k]++; 47 while(i <= top && sta[i] <= j)i++; 48 break; 49 } 50 } 51 } 52 printf("%d\n",ans); 53 } 54 } 55 return 0; 56 } 57 /**/
E. Erase Subsequences
题意:给两个字符串,问第二个字符串s2能不能拆成两部分(可以为空)使得这两部分是第一个字符串s1的两个不相交子串。
思路:枚举s2的分界点l,然后进行dp,f[i][j]表示s1到第i位,s2的第一部分到第j位时s2的第二部分最多到哪一位,转移时分三种,s1[i+1]=s2[j+1],由f[i][j]更新f[i+1][j+1];s1[i+1]=s2[l+f[i][j]+1],由f[i][j]+1更新f[i+1][j];f[i][j]直接更新f[i+1][j]。然后判断f[n][l]=m-l是否成立即可。需要注意的是要区分无法达到和最多到第0位的情况,这两种的区别在于那j位是否能匹配上。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define dl double 4 void rd(int &x){ 5 x=0;int f=1;char ch=getchar(); 6 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 7 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 8 } 9 void lrd(LL &x){ 10 x=0;int f=1;char ch=getchar(); 11 while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();} 12 while(ch<='9' && ch>='0')x=x*10+ch-'0',ch=getchar();x*=f; 13 } 14 const int INF=1e9; 15 const LL LINF=1e18; 16 const int N=405; 17 using namespace std; 18 int T; 19 char s1[N],s2[N]; 20 int n,m; 21 int f[N][N]; 22 int main(){ 23 // freopen("in.txt","r",stdin); 24 rd(T); 25 while(T--){ 26 scanf("%s%s",s1+1,s2+1);n=strlen(s1+1);m=strlen(s2+1); 27 bool flg=0; 28 for(int l=1;l<=m;l++){ 29 for(int i=0;i<=n;i++) 30 for(int j=0;j<=min(i,l);j++) 31 if(j)f[i][j]=-1;else f[i][j]=0; 32 for(int i=0;i<n;i++){ 33 for(int j=0;j<=min(i,l);j++){ 34 if(f[i][j] == -1)continue; 35 if(j+1 <= min(i+1,l) && s1[i+1] == s2[j+1])f[i+1][j+1]=max(f[i+1][j+1],f[i][j]); 36 if(s1[i+1] == s2[l+f[i][j]+1])f[i+1][j]=max(f[i+1][j],f[i][j]+1); 37 f[i+1][j]=max(f[i+1][j],f[i][j]); 38 } 39 } 40 if(f[n][l] == m-l){flg=1;break;} 41 } 42 if(flg)printf("YES\n");else printf("NO\n"); 43 } 44 return 0; 45 } 46 /**/
反思:学会降维的思想,这道题直观思路可以是f[i][j][k]用bool型来表示是否能够达到,这样时n^4,而降维并将一维变成数组中储存的值之后,复杂度会降到n^3。