涵盖知识点:贪心、dp、模拟、二进制etc.
比赛链接:
Educational Codeforces Round 82 (Rated for Div. 2)
A:Erasing Zeroes
题意:至少删几个0使得一个0-1串内所有的1都连续。
题解:记录第一个1的位置和最后一个1的位置输出中间0的个数即可
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int main(){ 5 int t; 6 cin>>t; 7 while(t--){ 8 string s; 9 cin>>s; 10 int st=-1,ed=-1; 11 for(int i=0;i<s.length();i++){ 12 if(s[i]=='1'){ 13 ed=i; 14 if(st==-1){ 15 st=i; 16 } 17 } 18 } 19 if(st==-1){ 20 cout<<0<<"\n"; 21 continue; 22 } 23 int res=0; 24 for(int i=st;i<=ed;i++){ 25 if(s[i]=='0') 26 res++; 27 } 28 cout<<res<<"\n"; 29 } 30 return 0; 31 }
B:National Project
题意:长度为n的道路,每天可以修的长度为1或选择不修,g天好天气和b天坏天气交替出现,要求至少n/2的路是在好天气下修的,问最少几天修完。
题解:先根据间隔算出遇到n/2天好天气总共需要几天,然后在输出长度和天数的较大值即可(要求路全部修完)。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 int main(){ 5 int t; 6 cin>>t; 7 while(t--){ 8 ll n,g,b; 9 ll res=0; 10 cin>>n>>g>>b; 11 ll mid=n/2+n%2; 12 ll gap=mid/g; 13 if(mid%g==0)gap--; 14 res+=gap*g; 15 res+=gap*b; 16 res+=(mid-gap*g); 17 cout<<max(res,n)<<"\n"; 18 } 19 return 0; 20 }
C:Perfect Keyboard
题意:求a-z一共26个字母排成一行的序列,要求给定的密码串的所有相邻字母在所求序列中也相邻。
题解:暴力模拟,每添加一个新的字符到序列中时用map记录下标,若序列中已经存在需要新添加的字符时判断是否符合条件,添加字符时判断是否卡死等情况分类讨论。
AC代码:(写的有点丑,可能有更好的写法或者解法)
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 map<char,int> mp; 5 6 int main(){ 7 int t; 8 cin>>t; 9 while(t--){ 10 mp.clear(); 11 string s; 12 cin>>s; 13 char res[110]; 14 for(int i=0;i<110;i++){ 15 res[i]='#'; 16 } 17 if(s.length()==1){ 18 cout<<"YES\n"; 19 for(char i='a';i<='z';i++) 20 cout<<i; 21 cout<<"\n"; 22 continue; 23 } 24 int l=30,r=31; 25 mp[s[0]]=30; 26 mp[s[1]]=31; 27 int idx=31; 28 res[30]=s[0]; 29 res[31]=s[1]; 30 bool flag=true; 31 for(int i=2;i<s.length();i++){ 32 if(s[i]==s[i-2]){ 33 idx=mp[s[i]]; 34 continue; 35 } 36 if(mp[s[i-1]]+1==mp[s[i]]||mp[s[i-1]]-1==mp[s[i]]){ 37 idx=mp[s[i]]; 38 continue; 39 } 40 if(mp[s[i]]!=0){ 41 flag=false; 42 //cout<<"chongfu\n"; 43 // cout<<i<<" "; 44 //cout<<mp['d']<<" "<<mp['o']<<endl; 45 break; 46 } 47 if(res[idx+1]=='#'){ 48 res[++idx]=s[i]; 49 mp[s[i]]=idx; 50 r=idx; 51 }else if(res[idx-1]=='#'){ 52 res[--idx]=s[i]; 53 mp[s[i]]=idx; 54 l=idx; 55 }else{ 56 flag=false; 57 //cout<<"kasi\n"; 58 break; 59 } 60 } 61 //cout<<l<<" "<<r<<" "; 62 //for(int i=l;i<=r;i++){ 63 //cout<<res[i]<<" "; 64 //} 65 if(flag){ 66 cout<<"YES\n"; 67 for(int i=l;i<=r;i++){ 68 cout<<res[i]; 69 } 70 for(char i='a';i<='z';i++){ 71 if(mp[i]==0) 72 cout<<i; 73 } 74 cout<<"\n"; 75 }else{ 76 cout<<"NO\n"; 77 } 78 } 79 return 0; 80 }
D:Fill The Bag
题意:给你m个二的倍数,每次操作可以将一个数拆成两半。问你最少几次操作能使n恰好等于其中某些数字的和。
题解:记录m个数的二进制位总和和n的二进制位,若存在相同位对应位数-1,否则将该位向上移动,每两个贡献合并为高一位的一个贡献。若为负数则向高位借用,记录借用的操作数量即为最终答案。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=1e5+10; 5 ll cnt[70]; 6 int main(){ 7 int t; 8 cin>>t; 9 while(t--){ 10 memset(cnt,0,sizeof cnt); 11 ll sum=0,n,m; 12 cin>>n>>m; 13 for(int i=1;i<=m;i++){ 14 int x; 15 cin>>x; 16 sum+=x; 17 int p=0; 18 while((x>>p)>1)p++; 19 cnt[p]++; 20 } 21 if(sum<n){ 22 cout<<"-1\n"; 23 continue; 24 } 25 int idx=0,ans=0; 26 while(n||cnt[idx]<0){ 27 cnt[idx]-=(n&1); 28 if(cnt[idx]<0){ 29 cnt[idx+1]--; 30 ans++; 31 }else{ 32 cnt[idx+1]+=(cnt[idx]>>1); 33 } 34 n>>=1; 35 idx++; 36 } 37 cout<<ans<<"\n"; 38 } 39 return 0; 40 }