【Codeforces #131 Div2】Solutions
【A System of Equations】
http://www.codeforces.com/contest/214/problem/A
题目大意:给定n,m,问有多少二元组(a,b)满足 ,其中a,b>0。
由于n很小,直接枚举a,b的值即可,复杂度O(n²)。
#include <iostream> using namespace std; int n,m; long long ans=0; int main(){ cin>>n>>m; for(int i=0;i<=1000;i++) for(int j=0;j<=1000;j++) if(i*i+j==n && i+j*j==m) ans++; cout<<ans<<endl; return 0; }
【B Hometask】
http://www.codeforces.com/contest/214/problem/B
题目大意:给出n个数字,要求用这n个数字中的一些拼成一个新数字,使新数字能被2,3,5整除,且新数字尽量大。
能同时被2,5整除的话,结尾必须是0,这样可以排除一种无解情况。
能被3整除的话,个位数字加起来必须是3的整数倍。
如果给出数字有0的话,我们就先放一个0到新数字的个位上,问题转化成,去掉尽量少的数(即选取尽量多的数),使得和能被3整除。
先假设所有数字都选取,和为sum,sum模3有三种情况:
1)sum mod 3=0 将数字从大到小排序即可。
2)sum mod 3=1 ①从数列中去除一个数x,使得x mod 3=1;
②若①无法实现则去除两个数i,j,使得(i+j) mod 3=1;
3)sum mod 3=2 ①从数列中去除一个数x,使得x mod 3=2;
②若①无法实现则去除两个数i,j,使得(i+j) mod 3=2;
剩下的数列就符合条件了。
为什么最多删掉两个数就能符合要求,那么删掉更多的数呢?
可以证明:若存在i,j,k,(i+j+k)mod 3=m (m=0,1,2), 一定存在x∈(i,j,k),且x≡(i+j+k)(mod 3),所以这种情况下只需要删除一个数。
代码很难看,重在思路……
#include <iostream> #include <cstdio> #include <algorithm> #include <cstdlib> #include <cstring> using namespace std; int a[100010],n; long long sum=0; bool flag=false; int vis[100]; int main(){ cin>>n; for(int i=1;i<=n;i++){ scanf("%d",&a[i]); vis[a[i]]++; sum+=a[i]; if(!a[i]) flag=true; } if(!sum){ cout<<"0"<<endl; return 0; } if(!flag){ cout<<"-1"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); int delta=sum%3; if(!delta){ for(int i=1;i<=n;i++) printf("%d",a[i]); cout<<endl; return 0; }else if(delta==1){ for(int i=1;i<=9;i++) if(vis[i] && i%3==1){ for(int j=1;j<=n;j++) if(a[j]==i){a[j]=0;break;} sum-=i; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n;i++) printf("%d",a[i]); cout<<endl; return 0; } for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) if((i+j)%3==1){ if(i==j && vis[i]>1){ for(int k=1;k<=n;k++) if(a[k]==i){a[k]=0,a[k+1]=0;break;} sum=sum-2*i; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n-1;i++) printf("%d",a[i]); cout<<endl; return 0; }else if(i!=j && vis[i] && vis[j]){ for(int k=1;k<=n;k++) if(a[k]==i){a[k]=0;break;} for(int k=1;k<=n;k++) if(a[k]==j){a[k]=0;break;} sum-=i,sum-=j; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n-1;i++) printf("%d",a[i]); cout<<endl; return 0; } } }else if(delta==2){ for(int i=1;i<=9;i++) if(vis[i] && i%3==2){ for(int j=1;j<=n;j++) if(a[j]==i){a[j]=0;break;} sum-=i; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n;i++) printf("%d",a[i]); cout<<endl; return 0; } for(int i=1;i<=9;i++) for(int j=1;j<=9;j++) if((i+j)%3==2){ if(i==j && vis[i]>1){ for(int k=1;k<=n;k++) if(a[k]==i){a[k]=0,a[k+1]=0;break;} sum=sum-2*i; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n-1;i++) printf("%d",a[i]); cout<<endl; return 0; }else if(i!=j && vis[i] && vis[j]){ for(int k=1;k<=n;k++) if(a[k]==i){a[k]=0;break;} for(int k=1;k<=n;k++) if(a[k]==j){a[k]=0;break;} sum-=i,sum-=j; if(!sum){ cout<<"0"<<endl; return 0; } sort(a+1,a+1+n,greater<int>()); for(int i=1;i<n-1;i++) printf("%d",a[i]); cout<<endl; return 0; } } } cout<<"-1"<<endl; return 0; }
【C Game】
http://www.codeforces.com/contest/214/problem/C
题目大意:有n个工作3台电脑,第i个工作需要在ci台电脑上完成。每一项工作还有很多“父工作”,必须在“父工作”都完成之后才能开始。工作花费1的时间,换电脑也需要时间,具体见题目描述,问最优策略的最短时间完成所有工作。
乍一看是一个挺麻烦的DP题,仔细想想这就是坑爹啊……三台电脑1→2,2→3,3→1的时间都是1,而其他的移动时间是2,这就是说如果你想1→3,还不如1→2→3,后者还能捎带着把需要2号电脑的工作给做了。其他的移动同理。这点明白之后,这就变成一个贪心题,电脑由1→2→3→1→2的顺序不停轮换,能做的工作就做,枚举最开始的电脑就行了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; template<class T>inline void gmin(T &a,T b){if(a>b)a=b;} int n,k,x,c[201],map[201][201],deg[201],tmp[201],ans=2147483647; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&c[i]); for(int i=1;i<=n;i++){ scanf("%d",&k); while(k--){ scanf("%d",&x); map[x][i]=1; tmp[i]++; } } for(int first_c=1;first_c<=3;first_c++){ int cur=first_c,rest=n,res=0; memcpy(deg,tmp,sizeof(deg)); while(rest){ for(int k=1;k<=n;k++){ for(int i=1;i<=n;i++) if(!deg[i] && c[i]==cur){ for(int j=1;j<=n;j++) if(map[i][j]) deg[j]--; deg[i]--,rest--; } } cur=cur%3+1; if(rest) res++; } gmin(ans,res); } ans+=n; printf("%d\n",ans); return 0; }
【D Numbers】
http://www.codeforces.com/contest/214/problem/D
题目大意:(又是Furik和Rubik)给定n,给定数列a[],要求构造新数字满足:长度不超过n位,且数字i在新数字中出现至少a[i]次。问这样的数有多少个。
比较简单的数位统计题,长度每增加一位,就插入一个数字,用组合数求。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; long long const BASE=1000000007ll; int n,a[11],sum; long long ans=0,f[11][101],C[101][101]; long long dp(int x){ memset(f,0,sizeof(f)); f[0][0]=1; for(int i=0;i<10;i++) for(int j=0;j<=x;j++) if(!f[i][j]) continue; else for(int k=a[i+1];k+j<=x;k++) f[i+1][j+k]=(f[i+1][j+k]+f[i][j]*C[j+k][k])%BASE; return f[10][x]; } int main(){ scanf("%d",&n); for(int i=1;i<=10;i++){ scanf("%d",&a[i]); sum+=a[i]; } C[0][0]=1; for(int i=1;i<=n;i++){ C[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j]+C[i-1][j-1])%BASE; } for(int bit=max(1,sum);bit<=n;bit++){ if(bit==1) ans+=sum==1?1:9; else{ for(int i=2;i<=10;i++){ int tmp=(a[i]!=0); a[i]-=tmp; ans=(ans+dp(bit-1))%BASE; a[i]+=tmp; } } } cout<<ans<<endl; return 0; }
【E Relay Race】
http://www.codeforces.com/contest/214/problem/E
题目大意:一个n×n的矩阵,找两条从(1,1)到(n,n)的路径使得权值和最大。每个格子权值只算一次,权值有负值。
当时贴了个费用流完挂……大半夜的迷迷糊糊得没心思写了……
不会的参考NOIP2000 提高组 方格取数
f[k][i][j]表示走了k步,A在第i行,B在第j行,A、B的具体坐标可以由行数和步数推知,这样就压成了三维就过了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> using namespace std; int f[601][301][301],a[301][301],n; int max(int a,int b){ if(a>b) return a; return b; } int gmax(int a,int b,int c,int d){ return max(max(max(a,b),c),d); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]); memset(f,0xf3,sizeof(f)); f[1][1][1]=a[1][1]; for(int k=2;k<=n*2-1;k++) for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) f[k][i][j]=gmax(f[k-1][i][j],f[k-1][i][j-1],f[k-1][i-1][j],f[k-1][i-1][j-1]), f[k][i][j]+=a[i][k-i+1]+(i==j?0:a[j][k-j+1]); printf("%d\n",f[2*n-1][n][n]); return 0; }