【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;
}

 

posted @ 2012-07-31 16:29  Delostik  阅读(319)  评论(0编辑  收藏  举报