QAQ:
A. Erasing Zeroes
题意:
给你一个01串,问最少删掉多少个0使得串中的所有1都相邻。
思路:
简单题,详细见代码。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; typedef long long LL; int num[maxn]; double E[maxn]; int main() { int T; cin >> T; while(T--) { string s; cin >> s; int len = s.size(); int i = 0,j = len-1; for( i = 0;i<len;++i) { if(s[i]=='1') break; } for( j;j>=0;--j) if(s[j]=='1') break; //-=cout <<i << " " <<j << endl; int num = 0; for(int k = i;k<=j;++k) { if(s[k]=='0') ++num; } cout << num<< endl; } return 0; }
B. National Project
题意:
你有n个盒子,你需要往里面去放物品,规则是这样的,必须从左到右开始放物品,你一天可以放一个物品,物品分为a,c两种,给你两个数g和b,代表你放数的规律是先放g天的物品a,然后在放b天的物品c,循环往复,某个盒子一旦被放置上物品以后就不能够被更改了,但是你可以在选择在某一天不放物品,求最少的天数能使得放置完了以后n个盒子中物品a的数目占到一半以上。
思路:
数学题,具体细节见代码。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; typedef long long LL; int num[maxn]; double E[maxn]; int main() { int T; cin >> T; while(T--) { LL n,g,b; cin >> n >> g >>b; LL tmp = (n+1)/2; LL ans = 0; if(tmp%g!=0) ans = tmp/(g)*(g+b)+tmp%(g); else ans = tmp/g*(g+b)-b; ans = max(ans,n); cout << ans << endl; } return 0; }
C. Perfect Keyboard
题意:
给你一个字符串s,要求你构造出一个a-z的全排列t,使得在s中相邻的两个字符在t中也相邻。可以输Yes和出t,不可以输出No。
思路:
考虑能构造的情况实际上就是,对于所有出现的字符,与他相邻的字符种类数不能超过两个,就可以根据s串构造出一个答案链,链的开头以及结尾都是一个只有一个字母相邻,中间则是有两个字母与其相邻,例如s = abcab,此时每个字母都有两个字母与其相邻,无论怎样改变a,b,c的相对位置,都没办法构造出满足条件的t串。
代码:
#include <bits/stdc++.h> using namespace std; const int maxn = 2e5+10; typedef long long LL; bool vis[30]; set<int>st[30]; int main() { int T; cin >> T; while(T--) { string s; cin >> s; for(int i = 0;i<=26;++i) { vis[i] = false; st[i].clear(); } int len = s.size(); if(len ==1) { printf("YES "); printf("%c",s[0]); for(int i = 0;i<26;++i) { if(!vis[i]&&i!=s[0]-'a') printf("%c",i+'a'); } printf(" "); continue; } bool flag = true; for(int i = 0;i<len;++i) { vis[s[i]-'a'] = true; if(i!=0) { st[s[i]-'a'].insert(s[i-1]-'a'); } if(i!=len-1) { st[s[i]-'a'].insert(s[i+1]-'a'); } if(st[s[i]-'a'].size()>2) { //cout << i << endl; flag = false; break; } } if(!flag) { printf("NO "); continue; } int id = -1; for(int i =0;i<=25;++i) { if(st[i].size()==1) { id = i; break; } } if(id==-1) { printf("NO "); continue; } int k = 0; string ans = ""; ans+=(id+'a'); ++k; id = *st[id].begin(); set<int>::iterator it; //cout << id << " " << ans << endl; while(st[id].size()!=1) { ans+=(id+'a'); ++k; for(it = st[id].begin();it!=st[id].end();++it) { if(*it!=(ans[k-2]-'a')) { id = *it; break; } } // cout << id << " " << ans << endl; } ans+=(id+'a'); for(int i = 0;i<26;++i) { if(!vis[i]) ans+=(i+'a'); } printf("YES "); cout << ans << " "; } return 0; }
D. Fill The Bag
题意:
你有一个大小为n的包,你还有m个物品,对于第i个物品它的大小是ai,ai一定是2的k次幂,k是整数,你可以把某个物品分成等大的两个部分,这会增加一次分割次数。问用这m个物品去填满这个背包所需要的最小分割次数是多少,如果不可能填满,则输出-1。
思路:
首先,因为物品可以被分隔,只要这些物品的总和大于等于n,那么就一定能填满背包。反之,就输出-1。
因为每一个数都是2的k次幂,所以考虑根据二进制来解决问题,将每一个数转化为他的二进制形式,我们可以得到两个数组a,b,a[i] 表示n的二进制表示中第i位的的值,b[i]表示在m个物品中第i位为1的数有多少个。接下来从低位到高位去遍历,明显,a[i]等于1时代表这一位需要从b中同样在这一位上构造出一个1来,设当前a[i] = 1,b数组中小于等于第i位的所有数的和为sum,当sum>=2^i时,为了分割数最小,所以选择用小于等于第i位的所有数来拼凑出第i位为1的情况,反之,则必须要从比第i位高的其他位借位来拼凑出第i位1的情况。相对应的更新分割次数以及相关值即可,详细细节可以看代码。
代码:
#include <bits/stdc++.h> using namespace std; typedef long long LL; LL n,m; LL num1[70],num2[70]; int main() { int t; cin >> t; while(t--) { cin >> n >> m; for(int i = 0;i<=60;++i) { num1[i] = num2[i] =0; } LL x; LL sum = 0; for(int i = 0;i<m;++i) { cin >> x; sum+=x; for(LL j = 0;j<=60;++j) { if(x&(1ll<<j)) { ++num1[j]; } } } for(LL j =0;j<=60;++j) { if(n&(1ll<<j)) { num2[j] =1; } } if(sum<n) { cout << -1 << " "; continue; } sum=0; for(int i = 0;i<=60;++i) { if(num1[i]&&num2[i]) { num1[i] -=1; num2[i] = 0; } } int ans = 0; for(LL i = 0;i<=60;++i) { sum+=((LL)num1[i]*(1ll<<i)); // cout << sum << " " << ans << " " << i<< " "; if(num2[i]) { if(sum>=(1ll<<i)) { sum-=(1ll<<i); num2[i] = 0; } else { LL j = i+1; for(j;!num1[j];++j); num1[j]-=1; for(LL k = j-1;k>=i;--k) { ++ans; ++num1[k]; } sum+=(1ll<<i); } } } cout << ans << " "; } return 0; }