地址:http://codeforces.com/contest/1335
题意:将n分成a,b。保证a>b,问有几种分法
解析:偶数输出n/2-1,奇数n/2即可
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> using namespace std; typedef long long ll; int main() { int t; cin>>t; while(t--) { ll n; cin>>n; if(n%2==0) cout<<n/2-1<<endl; else cout<<n/2<<endl; } }
题意:输出一个长为n的字符串,要求a长度里有b个不同字母
解析:其实弄来弄去还是只看一个len=a的串即可,比如abcabc,a=3, 从i=1到i=3和从i=2到i=4其实是同一个串而已,只是顺序不同罢了。所以根据要求构造成一个len=a,含有b个不同字符的串,然后把这个串依次往后接即可。自己写的有点麻烦......
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<set> using namespace std; typedef long long ll; char ch[2005],s[2005]; int main() { int t; cin>>t; while(t--) { int n,a,b; cin>>n>>a>>b; if(a==b) { int k=0; for(int i=0;i<n;i++) { char mid=(char)(k+'a'); k++; if(k==26) k=0; cout<<mid; } cout<<endl; } else { int k=0; int cnt=0; int mid; char md; for(int i=0;i<a;i++) { ch[i]=(char)(k+'a'); cnt++; if(cnt==b) { mid=i; md=ch[i]; break; } k++; } for(int i=mid+1;i<a;i++) ch[i]=md; int tot=0; for(int i=0;i<n;i++) { cout<<ch[tot]; tot++; if(tot==a-1) tot=0; } cout<<endl; } } }
题意:在一个数组中挑些数分成两组,一组各不相等,一组全相等。
解析:先统计出两个值,cnt:有多少种数。maxx:出现次数最多的数。然后把cnt--,这个cnt就是除了maxx以外有多少种数了。
然后分情况:1:cnt==maxx,没得说,直接输出cnt
2:maxx<cnt。没法进行补救,一组全相等我最大也就分出maxx,没法进行补救措施。这里给出样例:2 2 2 3 3 4 7 8 9供测试证明。
3:maxx>cnt。是有可能进行补救的。因为cnt是不包含maxx的,所以可以从maxx挪出来一个放入cnt中,但是要保证两组的元素数目相同,所以要看看maxx-1和cnt+1的大小关系。如果cnt+1>maxx-1了,那肯定是不能往里补的,只能输出cnt。能补的话,就输出cnt+1。给出两个样例:1 2 4 4 4 4 3和2 2 2 2 2 7 4。这俩一个能补一个不能补,可以自行测试一下。
#include<iostream> #include<cstdio> #include<cmath> #include<map> #include<algorithm> #include<cstring> using namespace std; typedef long long ll; const int maxn=2e5+10; int a[maxn]; int main() { int t; cin>>t; while(t--) { int n; cin>>n; map<int,int>mp; int maxx=-1,cnt=0; for(int i=0;i<n;i++) { cin>>a[i]; if(mp[a[i]]==0) cnt++; mp[a[i]]++; maxx=max(maxx,mp[a[i]]); } cnt--; // cout<<cnt<<"--"<<maxx<<endl; if(maxx==cnt) { cout<<maxx<<endl; continue; } else if(maxx<cnt) { cout<<maxx<<endl; } else { if(cnt+1>maxx-1) cout<<cnt<<endl; else cout<<cnt+1<<endl; } } }
题意:给你一个正确的9*9数独矩阵。对它改动不超过9次来实现每行每列每3*3都要有至少两个元素相同。
解析:这个重在对题意的理解。可以看出,给出的一定是每行每列每3*3都没有重复元素的。所以要让它变成反数独矩阵,只需要任意找一个数x,把所有的x变成y(x!=y)就可以了。这样,保证了每行每列都有重复元素,而对于3*3,已知输入的3*3里一定没有重复元素,那么1-9都出现在了3*3里,我们把x变成y,一定会出现重复。
#include<bits/stdc++.h> using namespace std; const int maxn=10; char s[maxn][maxn]; int main() { int t; cin>>t; while(t--) { for(int i=0;i<9;i++) cin>>s[i]; for(int i=0;i<9;i++) { for(int j=0;j<9;j++) { if(s[i][j]=='3') s[i][j]='4'; } } for(int i=0;i<9;i++) cout<<s[i]<<endl; } }
题意:通过任意删除某些元素,来求出最大的aaa....bbb......aaaa(回文)的2x+y来。
解析:这个题只要把方法想好,直接可以过E2的hard。
在输入时,用vector来记录每个元素出现的位置。然后来枚举每一个出现次数>1的数,比如当前枚举的数字x的下标为 L1,L2,L3,R1,R2,R3。那么我们需要枚举L3-R1之间的数字,由于a<=26,所以用Num[26]来记录每个数字出现的次数,找出出现次数最多的数字mx,那么答案就更新为max(maxx,mx+2*mid)。这个mid=size/2,size为当前枚举的数字x出现了几次。这个操作,也许会通过一些样例,但还远远不够。
for(int i=1;i<=26;i++) { int size=v[i].size(),mid; if(size==1) maxx=max(maxx,size); if(size<=1) continue; mid=size/2; int num[30]={0},mx=0; for(int j=v[i][mid-1]+1;j<v[i][size-mid];j++) { num[a[j]]++; } for(int j=1;j<=26;j++) mx=max(mx,num[j]); maxx=max(maxx,mx+mid*2); }
其实这里也是两个贪心策略,len=2*x+y,x取最大是一个部分,y取最大又是一个部分,要想len最大,两个贪心都要弄一遍,才算完整。
给出样例1 2 2 1 2 2 1 2 2 1,根据上一段的做法,得的结果是6,而实际上是要把中间两个1去掉,答案为 1 2 2 2 2 2 2 1,len=8。因为上段的思想是:贪心,两侧数字的数目取最大。取的只是 1 1 2 2 1 1,这个len=6。当以2出现为两边时,中间两个2之间没有元素,为空,所以len=6。所以对于每次枚举的v[x][],还需要以中间向两边扩散,枚举中间空的其他元素出现次数Num[]
第一次枚举1,1处,然后枚举2,2处.......v[x][]这个x的数目一直在减少,中间的数目一直在增加。注意这里的下标变化:
for(int j=mid-1;j>0;j--) //j即为当前一半的长度 { for(int c=v[i][j-1]+1;c<v[i][j];c++) //左 num[a[c]]++; for(int c=v[i][size-j-1]+1;c<v[i][size-j];c++)//右 num[a[c]]++; for(int c=1;c<=26;c++) mx=max(mx,num[c]); maxx=max(maxx,mx+j*2); //更新最大值 }
#include<iostream> #include<cstdio> #include<cmath> #include<map> #include<algorithm> #include<cstring> #include<vector> using namespace std; typedef long long ll; const int maxn=2e3+10; int a[maxn]; vector<int>v[30]; int main() { int t; cin>>t; while(t--) { int n; for(int i=1;i<=26;i++) //注意清空 v[i].clear(); cin>>n; for(int i=0;i<n;i++) { cin>>a[i]; v[a[i]].push_back(i); } int maxx=-1; for(int i=1;i<=26;i++) //枚举位于两侧的数字 { int size=v[i].size(),mid; if(size==1) maxx=max(maxx,size); //单种数字做为答案 if(size<=1) continue; mid=size/2; //一侧数量 int num[30]={0},mx=0; for(int j=v[i][mid-1]+1;j<v[i][size-mid];j++) { num[a[j]]++; } for(int j=1;j<=26;j++) mx=max(mx,num[j]); maxx=max(maxx,mx+mid*2);//两侧取最大的贪心 for(int j=mid-1;j>0;j--) { for(int c=v[i][j-1]+1;c<v[i][j];c++) //左区间 num[a[c]]++; for(int c=v[i][size-j-1]+1;c<v[i][size-j];c++) //右区间 num[a[c]]++; for(int c=1;c<=26;c++) mx=max(mx,num[c]); maxx=max(maxx,mx+j*2);//中间取最大的贪心 } } cout<<maxx<<endl; } }
E2:
题意:E2和E1的不同就是范围了。
解析:直接用上面那套代码,范围改一下就可以了。
#include<iostream> #include<cstdio> #include<cmath> #include<map> #include<algorithm> #include<cstring> #include<vector> using namespace std; typedef long long ll; const int maxn=2e5+10; int a[maxn]; vector<int>v[202]; int main() { int t; cin>>t; while(t--) { int n; for(int i=1;i<=200;i++) v[i].clear(); cin>>n; for(int i=0;i<n;i++) { cin>>a[i]; v[a[i]].push_back(i); } int maxx=-1; for(int i=1;i<=200;i++) { int size=v[i].size(),mid; if(size==1) maxx=max(maxx,size); if(size<=1) continue; mid=size/2; int num[205]={0},mx=0; for(int j=v[i][mid-1]+1;j<v[i][size-mid];j++) { num[a[j]]++; } for(int j=1;j<=200;j++) mx=max(mx,num[j]); maxx=max(maxx,mx+mid*2); for(int j=mid-1;j>0;j--) { for(int c=v[i][j-1]+1;c<v[i][j];c++) num[a[c]]++; for(int c=v[i][size-j-1]+1;c<v[i][size-j];c++) num[a[c]]++; for(int c=1;c<=200;c++) mx=max(mx,num[c]); maxx=max(maxx,mx+j*2); } } cout<<maxx<<endl; } }