Particle

競技プログラミングについての雑記

Codeforces Round #131 (Div. 2)

久々にCFに出てみました。

A.
全探索。
余計なことを考えずに、a,bが0から1000までの場合をすべて確かめる。

int main(){
	int n,m,ans=0;
	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.
ベタベタ書きました。

0がない場合 -> -1
和を3で割った余りが1の場合 -> 余りが1のやつを1つ消す。出来なければ、余りが2のやつを2つ消す。どちらも、小さい順に見ていく
和を3で割った余りが2の場合 -> 上と同じような感じ
3で割り切れる場合 -> そのまま

その後、0以外何も残らなかったら0を1つだけ出力して、それ以外の場合は数字が大きい方から出力する。

int d[10];

int main(){
	int n;
	cin>>n;
	for(int i = 0; i < n; i++){
		int a;
		cin>>a;
		d[a]++;
	}
	int sum=0;
	if(!d[0]){
		cout<<-1<<endl;
		return 0;
	}
	for(int i = 0; i < 10; i++){
		sum += d[i]*i;
	}
	if(sum%3==1){
		if(d[1])d[1]--;
		else if(d[4])d[4]--;
		else if(d[7])d[7]--;
		else if(d[2]+d[5]+d[8]<2){
			cout<<0<<endl;
			return 0;
		}else {
			int t=2;
			while(t){
				if(d[2])d[2]--;
				else if(d[5])d[5]--;
				else if(d[8])d[8]--;
				t--;
			}
		}
	}
	if(sum%3==2){
		if(d[2])d[2]--;
		else if(d[5])d[5]--;
		else if(d[8])d[8]--;
		else if(d[1]+d[4]+d[7]<2){
			cout<<0<<endl;
			return 0;
		}else {
			int t=2;
			while(t){
				if(d[1])d[1]--;
				else if(d[4])d[4]--;
				else if(d[7])d[7]--;
				t--;
			}
		}
	}
	sum = 0;
	for(int i = 0; i < 10; i++){
		sum += d[i]*i;
	}
	for(int i = 9; i >= 0; i--){
		for(int j = 0; j < d[i]; j++){
			if(i!=0||sum)cout<<i;
			else{
				cout<<0;
				break;
			}
		}
	}
	
	cout<<endl;
	return 0;
}

C.
D.
E.
hackしてたら時間が足りなくて解けなかった。
CとEは解けるかもしれない問題だったから、解いたほうが良かったかもしれない。

C.(追記)
読解が難しそうで、配点が大きいから難しいと思い込んでいましたが、解いてみたら簡単でした。2000点だったので、かなりもったいない。仕方がないことですが。

コンピュータは3つしかないから、最初どれにするかは全て試す。
その後、1→2→3→1の順で移動して、更新できなくなるまで更新する。
一番良い解を出力する。

int c[200],n;
vector<int> d[200];
bool used[200];

int solve(int t){
	memset(used,0,sizeof(used));
	int uc=0,ans=0;
	while(uc<n){
		bool udf=true;
		while(udf){
			udf=false;
			for(int i = 0; i < n; i++){
				if(c[i]==t){
					bool flag=!used[i];
					for(int j = 0; j < d[i].size(); j++){
						if(!used[d[i][j]]){
							flag=false;
							break;
						}
					}
					if(flag){
						used[i]=true;
						udf=true;
						ans++;
						uc++;
					}
				}
			}
		}
		t=t%3+1;
		ans++;
	}
	return ans-1;
}

int main(){
	int ans=1<<10;
	cin>>n;
	for(int i = 0; i < n; i++)cin>>c[i];
	for(int i = 0; i < n; i++){
		int k;
		cin>>k;
		for(int j = 0; j < k; j++){
			int a;
			cin>>a;
			d[i].push_back(a-1);
		}
	}
	for(int i = 1; i <= 3; i++)ans=min(ans,solve(i));
	cout<<ans<<endl;
	return 0;
}

E.(追記)
DPです。
4次元配列を3次元配列にする発想が無くて解けませんでしたが、実装は難しくありませんでした。

2人とも(0,0)からスタートすると考えて、2人とも同時に動かしていく。歩く距離は2人とも常に同じなので、配列の次元を1次元下げられる。
DP[1人目のx座標][1人目のy座標][2人目のx座標]:=(0,0)からその2点まで移動したときの得点の最大値
とすると、2人目のy座標が分かる。
2人目のx座標は常に1人目のx座標以下になるようにして(しなくても大丈夫かもしれない)、同じ点に行くときだけ2回分数えないようにすれば解ける。

int g[300][300],dp[300][300][300],dx[]={1,0},dy[]={0,1};
const int INF = 1<<28;

int main(){
	int n;
	cin>>n;
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			cin>>g[i][j];
		}
	}
	for(int i = 0; i < n; i++){
		for(int j = 0; j < n; j++){
			for(int k = 0; k < n; k++){
				dp[i][j][k]=-INF;
			}
		}
	}
	
	dp[0][0][0]=g[0][0];
	for(int i = 0; i < 2*n-2; i++){
		for(int j = max(0,i-n+1); j <= min(i,n-1); j++){
			for(int k = max(0,i-n+1); k <= j; k++){
				for(int l = 0; l < 4; l++){
					int x1=j+dx[l/2],y1=i-j+dy[l/2],x2=k+dx[l%2],y2=i-k+dy[l%2];
					if(x1<n&&y1<n&&x2<n&&y2<n){
						if(x1==x2){
							dp[x1][y1][x2]=max(dp[x1][y1][x2],dp[j][i-j][k]+g[x1][y1]);
						}else {
							dp[x1][y1][x2]=max(dp[x1][y1][x2],dp[j][i-j][k]+g[x1][y1]+g[x2][y2]);
						}
					}
				}
			}
		}
	}
	cout<<dp[n-1][n-1][n-1]<<endl;
	return 0;
}

感想:hackもぐもぐ