Codeforces Round #513 by Barcelona Bootcamp (rated, Div. 1 + Div. 2)
A.Phone Numbers
题意:给你n个数字,每个数字最多只能用一次,问你最多能组成以8开头的11位电话号码有多少个
思路:模拟即可,注意char数组读入是从0下标开始的(在这里被hack了...)
1 #include<bits/stdc++.h> 2 int main() 3 { 4 int n,num=0,ans=0; 5 char c[105]; 6 scanf("%d%s",&n,c); 7 for(int i=0;i<n;i++)if(c[i]=='8')num++;//原来是从1到n... 8 while(num>0&&n>=11) 9 { 10 num--; 11 n-=11; 12 ans++; 13 } 14 printf("%d\n",ans); 15 return 0; 16 }
B. Maximum Sum of Digits
题意:设正整数a,b和为n,现给定n,求每位数字和最大的一组(a,b),这个和为多少
思路:由于是求每位数字的和,所以具体地考虑对一个具体数位的分割,发现对于一个确定的数ai,无论怎样分,是否借位/被借位,其和都是ai,而对于不同分法产生差异的原因在于是否借位
由此可以记忆化dfs,设f(id,bor,Now)表示到第id位,是否被借位(0,1),当前数码和为Now
要注意借位与不借位情况的判定(原来来少考虑了没有借位过的9不能借位,被hack了)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int ans=0,a[15]={0}; 4 int Mark[15][5][255]={0}; 5 int dfs(int id,int bor,int Now) 6 { 7 if(id==a[0]+1) 8 { 9 ans=max(ans,Now); 10 return ans; 11 } 12 13 if(Mark[id][bor][Now]!=-1)return Mark[id][bor][Now]; 14 15 int tmp=0; 16 if(a[id]!=0)tmp=max(tmp,dfs(id+1,0,Now+a[id]-bor)); 17 //µ±Ç°Î»²»ÎªÁãÔò¿¼ÂDz»ÏòÏÂһλ½èλµÄÇé¿ö£¨Îª0Ôò½èλ±ØÈ»¸üÓÅ£¬ÇÒ°üº¬Á˱»½èλµÄÇé¿ö£© 18 if(id!=a[0]&&(!(a[id]==9&&bor==0)))tmp=max(tmp,dfs(id+1,1,Now+a[id]+10-bor)); 19 //¸ÃλÖò»Îª×î¸ßλÇÒ²»ÎªÃ»±»½èλ¹ýµÄ9£¬Ôò¿¼ÂÇÏòÏÂһλ½èλ 20 //ÔÀ´ÉÙ¿¼ÂÇÁËûÓнèλ¹ýµÄ9²»Äܽèλ 21 22 return Mark[id][bor][Now]=tmp; 23 } 24 int main() 25 { 26 long long n; 27 scanf("%lld",&n); 28 while(n) 29 { 30 a[++a[0]]=n%10; 31 n/=10; 32 } 33 memset(Mark,-1,sizeof(Mark)); 34 dfs(1,0,0); 35 printf("%d\n",ans); 36 return 0; 37 }
C. Maximum Subrectangle
题意:给定长度为n的数组a和长度为m的数组b,求满足ai*bj<=x(其中i从x1变化到x2,j从y1变化到y2)的使x1,x2,y1,y2表示的矩形的最大面积
思路:先化简式子,可以把限制条件化简为(sa2-sa1)*(sb2-sb1)<=x,考虑到a和b都是长为2000的数组,把这四个前缀和都枚举一遍肯定是不行的
这时可以用贪心的思想,我们只需要求得同一个数组在相同长度下前缀和之差的最小值即可,然后再把(sa2-sa1)*(sb2-sb1)的两个乘法因子分别按照顺序和倒序进行枚举即可
这样做的时间复杂度使o(n^2)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int sa[2005]={0},sb[2005]={0},fa[2005]={0},fb[2005]={0}; 4 int main() 5 { 6 int n,m,tmp,x; 7 scanf("%d%d",&n,&m); 8 for(int i=1;i<=n;i++) 9 { 10 scanf("%d",&tmp); 11 sa[i]=sa[i-1]+tmp; 12 fa[i]=1e9; 13 } 14 for(int i=1;i<=m;i++) 15 { 16 scanf("%d",&tmp); 17 sb[i]=sb[i-1]+tmp; 18 fb[i]=1e9; 19 } 20 scanf("%d",&x); 21 22 for(int i=1;i<=n;i++) 23 for(int j=i;j<=n;j++)fa[j-i+1]=min(fa[j-i+1],sa[j]-sa[i-1]); 24 25 for(int i=1;i<=m;i++) 26 for(int j=i;j<=m;j++)fb[j-i+1]=min(fb[j-i+1],sb[j]-sb[i-1]); 27 28 int i=1,j=m,ans=0; 29 while(i<=n&&j>=1) 30 { 31 if(fa[i]<=x/fb[j]) 32 { 33 ans=max(ans,i*j); 34 i++; 35 } 36 else j--; 37 } 38 39 printf("%d\n",ans); 40 return 0; 41 }
D. Social Circles
题意:n个人摆宴席,每个人左边和右边都分别至少有li和ri个空位,桌子的个数不限,也可以一个人一桌,求最少的板凳数
思路:当时察觉到了是贪心算法,但并没有想出来
看了别人的代码是把l,r读入后分别排序,按小小对应,大大对应的规则直接求出答案
在这里验证一下正确性:
- 只考虑两个人,设其属性为(l,r)和(l',r'),且有l' > l和r' < r,那么max(l’,r’)+max(l,r)一定不小于max(l’,r)+max(l,r’),(可以画图验证),且必然对应了一种摆桌方案
- 考虑n个人,如果有两个人没有满足小小对应,大大对应,那么他们交换了之后一定会更优,这就证明了读入后直接排序的正确性
- 而对于这样的配对方案,又一定存在一种摆桌方案,证明:设(ai,bj)为某人的属性,若ai=bj则自己围一桌即可,否则,必有另一人(aj,bk)与其右边合并,这样就可以把这两个人看做一个人(ai,bk)...这样一直合并,最后一定可以合并成(ai,bi)
- 综上,这样的贪心算法是正确的