Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2011-10-01

A

02:19 | A - kojingharangの日記 を含むブックマーク はてなブックマーク - A - kojingharangの日記 A - kojingharangの日記 のブックマークコメント

最後にw番目になるカードだけわかればいいので最後から遡って追跡する。

問題が「w番のカードは最後にどの位置にあるか」と早とちりしてしまい、何回か誤答。問題文日本語なのに......



B

02:19 | B - kojingharangの日記 を含むブックマーク はてなブックマーク - B - kojingharangの日記 B - kojingharangの日記 のブックマークコメント

いろいろ考えたところ、貪欲法でいけそうとなったのでやってみたらうまくいった。

満足度が高い順に、消費期限の日から遡って飲む予定をできるだけいれて行く。すでに予定が入っている日はスキップする。

(なぜなら既に入っているコーヒーの満足度は入れようとしているものより満足度が同じか高いから。)

以上をやればいいが、largeの日数は単純にループで回してたら終わらないくらい多いので、連続する予定を日数に関係ないオーダーで記録する工夫が必要となる。

具体的には、空いてる日を「開始日と長さのリスト」で管理するようにして、そこに予定をまとまて入れるようにする。

空きリストが[(1,5)]なら1日目から5日分空いてるという状態。そこに4日目から前方向に2日分予定を入れると空きリストは[(1,2),(5,1)]となる。

一般的にはリストを後ろから見ていき、置けるところに置いてリストを更新していけばいい。オーダーはリストの長さになる。

1回の予定挿入でリストの要素数は高々1個増えるだけなので、リストの要素数は最大でもコーヒーの種類数程度。なので日数が1e18とかでも現実的な時間で計算できる。

予定を入れられた分だけ満足度を加算していって、全種類入れ終わったらそれが答え。


C

02:19 | C - kojingharangの日記 を含むブックマーク はてなブックマーク - C - kojingharangの日記 C - kojingharangの日記 のブックマークコメント

ふつうにやると large の計算が終わらないので、なんか法則性があってO(N)にならないような方法があるんだろう

→とりあえずビットが全部たってる2**n-1とそれ以外で分けたらいいんじゃね?......という予想をしつつも証明できないのでまずはsmallをふつうにやる。

smallが通ったら、N以下の最大の2**n-1と残りでわけたときの合計ビット数を求めてみて、通った答えと一致してることを確認

→でdiffしたらおなじだったのでそれでlarge提出。なんとも危なっかしいやり方だけどまあいいや(笑)

証明は「2**n-1で分けるのが最適でない場合、ビットが全部立ってるとこから残りの方に移すとビットがもっと増えるはずだがそうはならない」みたいな感じでできそう。

立ってるビットを減らして他方にもってきたときに他方で2以上増えないといけないけど、0だったとこにもってきても1になるだけで合計は変わらないし、1だったとこにもってくると0になって良くて繰り上がって1になっても変わらないし繰り上がり方によってはどんどん減る方向にしかいかないので当初の分け方が最大。みたいな感じでしょうか。

2011-05-22

Google Code Jam 2011 Round1C -> 40pt(通過)

01:19 | Google Code Jam 2011 Round1C -> 40pt(通過) - kojingharangの日記 を含むブックマーク はてなブックマーク - Google Code Jam 2011 Round1C -> 40pt(通過) - kojingharangの日記 Google Code Jam 2011 Round1C -> 40pt(通過) - kojingharangの日記 のブックマークコメント

結果 |OO|O-|O-| 40pt, rank 736 でした。ぎりぎり通過。ほっと一息。

A. Square Tiles

01:19 |  A. Square Tiles - kojingharangの日記 を含むブックマーク はてなブックマーク -  A. Square Tiles - kojingharangの日記  A. Square Tiles - kojingharangの日記 のブックマークコメント

  • 青と白のタイルがグリッド状に敷き詰められている。青のとこだけ全部2x2の大きい赤タイルに置き換えたい。全部変えられる?
  • こういうやるだけ問題(シミュレーション)が 10 分ちょいでできるようになったのは大きな進歩である。。
  • 残り時間たっぷりの状態で残りの2問に取り掛かれるのは精神的にもよいです。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int R,C;
		cin>>R>>C;
		string v[R];
		REP(y, R)
		{
			cin>>v[y];
		}
		int ok=1;
		REP(y, R)
		{
			REP(x, C)
			{
				if(v[y][x]=='#')
				{
					if(y==R-1||x==C-1||v[y][x+1]!='#' || v[y+1][x]!='#' || v[y+1][x+1]!='#')
					{
						ok=0;
						goto END;
					}
					v[y][x]=v[y+1][x+1]='/';
					v[y+1][x]=v[y][x+1]='\\';
				}
			}
		}
		END:;
		cout<<"Case #"<<t+1<<":"<<endl;
		if(ok)
		{
			REP(y, R)
			{
				cout<<v[y]<<endl;
			}
		}
		else cout<<"Impossible"<<endl;
	}
	return 0;
}

B. Space Emergency

01:19 |  B. Space Emergency - kojingharangの日記 を含むブックマーク はてなブックマーク -  B. Space Emergency - kojingharangの日記  B. Space Emergency - kojingharangの日記 のブックマークコメント

  • 0..N の星を順に移動したい。星間の距離は与えられる。1時間につき0.5単位進める。
  • ブーストを使うと1時間につき1単位進める。
  • ブーストは最大 L 個使えて、別々な星のところに置ける。
  • ブーストを準備するのに t 時間かかる。スタートと同時に全ブーストの準備を開始できる。
  • 最短何時間で星 N に辿りつける?
  • Small はブーストの数が最大2なので、総当りで。少し考えてアイデアが出なかったら泥臭く確実に点を取りに行く作戦。
  • L==0 の場合、L==1 の場合、L==2 の場合でコードを分けたりして相当泥臭いww
  • 着実に Small クリア。
  • Large は、0<=L<=N なので計算量がやばそう。
  • 基本的には距離が長いところから順にブーストを割り当てていけばいいが、t が大きい時はブーストが準備できずに通りすぎてしまう。
  • その辺考慮してどういう戦略がいいのか思い浮かばなかった。
  • C に行って戻ってきて、とりあえず使用ブースト数が L-1 のときの解にブーストを1個最適に追加したものが最適解になりそうな気がしたので1個ずつ追加していく O(N^2) のコードを書いたが Large じゃやっぱり終わらず。
  • 解説や上位陣のコードを見ると、単純に t 秒以降にいるエリアの中で距離でソートして短縮できる時間が大きい物順に L 個取るという方法で行けるみたい。
  • 言われてみるとそうだ orz
  • t 秒以降に行けるエリアは、その前にブーストを置くことで変わってしまうのではないかと思ってわざわざ難しく考えてしまったが、そもそも t 秒より前にブーストを置いても効果がないので杞憂であった。
  • このくらい解けないとまずいな....

int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(ttt, T)
	{
		ll L,t,N,C;
		cin>>L>>t>>N>>C;
		int v[N];
		REP(i, C) cin>>v[i];
		FOR(i, C, N) v[i]=v[i-C];
		//REP(i, N) cout<<v[i]<<endl;;
		
		ll mintt = -1;
		if(L==0)
		{
			ll tt=0;
			REP(i, N)
			{
				tt+=v[i]*2;
			}
			//cout<<"tt "<<tt<<endl;
			if(mintt==-1 || tt<mintt) mintt=tt;
		}
		else if(L==1)
		{
			FOR(ba, 0, N)
			{
				// use in ba
				ll tt=0;
				REP(i, N)
				{
					if(i==ba)
					{
						if(t<=tt) tt+=v[i];
						else if(t<tt+v[i]*2) tt+= (t-tt) + v[i]-(t-tt)/2;
						else tt+=v[i]*2;
					}
					else
					{
						tt+=v[i]*2;
					}
				}
				if(mintt==-1 || tt<mintt) mintt=tt;
			}
		}
		else
		{
			FOR(ba, 0, N-1)
			FOR(bb, ba, N)
			{
				// use in ba, bb
				ll tt=0;
				REP(i, N)
				{
					if(i==ba||i==bb)
					{
						if(t<=tt) tt+=v[i];
						else if(t<tt+v[i]*2) tt+= (t-tt) + v[i]-(t-tt)/2;
						else tt+=v[i]*2;
					}
					else
					{
						tt+=v[i]*2;
					}
				}
				if(mintt==-1 || tt<mintt) mintt=tt;
			}
		}
		
		cout<<"Case #"<<ttt+1<<": "<<mintt<<endl;
	}
	return 0;
}

C. Perfect Harmony

01:19 |  C. Perfect Harmony - kojingharangの日記 を含むブックマーク はてなブックマーク -  C. Perfect Harmony - kojingharangの日記  C. Perfect Harmony - kojingharangの日記 のブックマークコメント

  • 他の人が鳴らす音の周波数が分かっている。
  • 自分が鳴らす音を L~H の範囲で決めて、全員とハーモニーするようにしたい。
  • 2人がハーモニーしてるとは、2人が出す周波数が「どっちかがどっちかで割り切れる」ことである。
  • 8pt だし早速何人か解いてて正解率も高かったので問題を見てみると、Small だけなら総当りでいけそうなのでさくっと書いて点を取りに行く作戦。
  • Large は gcd と lcm を求めて L~H に入っているかどうか調べれば行けるかと思ったら全然そうではなくて、どっちかがどっちかで割り切れればよいので gcd~lcm の間にも解が有りうることに途中で気づいた。でお手上げ。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int N,L,H;
		cin>>N>>L>>H;
		int v[N];
		REP(i, N) cin>>v[i];
		
		int ok=0;
		int ans=0;
		FOR(f, L, H+1)
		{
			int lok=1;
			REP(i, N)
			{
				if(f<v[i])
				{
					if(v[i]%f!=0) {lok=0; break;}
				}
				else if(f%v[i]!=0) {lok=0; break;}
			}
			if(lok) {ok=1; ans=f; break;}
		}
		
		if(ok)
			cout<<"Case #"<<t+1<<": "<<ans<<endl;
		else
			cout<<"Case #"<<t+1<<": NO"<<endl;
	}
	return 0;
}

2011-05-21

Google Code Jam 2011 Round1B -> 20pt(通過できず)

04:19 | Google Code Jam 2011 Round1B -> 20pt(通過できず) - kojingharangの日記 を含むブックマーク はてなブックマーク - Google Code Jam 2011 Round1B -> 20pt(通過できず) - kojingharangの日記 Google Code Jam 2011 Round1B -> 20pt(通過できず) - kojingharangの日記 のブックマークコメント

結果 |00|--|--| 20pt, rank 1649 でした。Round1C まで居残りのはめに。。

A. RPI

04:19 |  A. RPI - kojingharangの日記 を含むブックマーク はてなブックマーク -  A. RPI - kojingharangの日記  A. RPI - kojingharangの日記 のブックマークコメント

  • チームリーグの勝敗結果(よくあるマトリックス)がある。
  • RPI なる指標を計算する。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int N;
		cin>>N;
		string sc[N];
		double WP[N];
		double OWP[N];
		double OOWP[N];
		CLR(WP);
		CLR(OWP);
		CLR(OOWP);
		REP(i, N){ cin>>sc[i]; }
		REP(i, N)
		{
			{
				int win=0, all=0;
				REP(j, N)
				{
					if(sc[i][j]!='.')
					{
						win += sc[i][j]=='1';
						all++;
					}
				}
				WP[i] = (double)win/all;
			}
			{
				int _all=0;
				REP(j, N)
				{
					if(sc[i][j]!='.')
					{
						_all++;
						// calc wp of j except i
						int win=0, all=0;
						REP(k, N)
						{
							if(k==i) continue;
							if(sc[j][k]!='.')
							{
								win += sc[j][k]=='1';
								all++;
							}
						}
						OWP[i] += (double)win/all;
					}
				}
				OWP[i] /= (double)_all;
			}
		}
		cout<<"Case #"<<t+1<<":"<<endl;
		REP(i, N)
		{
			int all=0;
			REP(j, N)
			{
				if(sc[i][j]!='.')
				{
					OOWP[i] += OWP[j];
					all++;
				}
			}
			OOWP[i] /= all;
			double RPI = 0.25 * WP[i] + 0.50 * OWP[i] + 0.25 * OOWP[i];
			cout<<RPI<<endl;
		}
		cout<<endl;
	}
	return 0;
}

B. Revenge of the Hot Dogs

04:19 |  B. Revenge of the Hot Dogs - kojingharangの日記 を含むブックマーク はてなブックマーク -  B. Revenge of the Hot Dogs - kojingharangの日記  B. Revenge of the Hot Dogs - kojingharangの日記 のブックマークコメント

  • 数直線上に点がいくつか与えられている。
  • すべての点は毎秒1m動くことができる。
  • 任意の2点が D[m] 以上離れた状態にするには何秒かかる?
  • (1) 同じ位置にある点クラスタ内で D 以上に広げる (2) クラスタ間で D 以上に広げる の2ステップで考えてサンプルも通りそうなので実装してみたものの、よく見るとそもそもサンプルが説明できないことに気づく orz
  • C を見て、戻ってきてやっぱりだめ。
  • 解説を見ると、時間を決め打ちして2分探索できるとのこと。思いつかなかった.....
  • 時間が決まればとりあえず左端からできるだけ遠くに行って詰めていけばいいので OK/NG がわかる。
  • 言われれば納得....
  • 昔、円周上を 10000 分割くらいして解いたら通ったみたいな問題があったのを思い出した。
  • しばらく考えてだめなら泥臭くいこ~

C. House of Kittens

04:19 |  C. House of Kittens - kojingharangの日記 を含むブックマーク はてなブックマーク -  C. House of Kittens - kojingharangの日記  C. House of Kittens - kojingharangの日記 のブックマークコメント

  • N 角形の頂点に色を塗る。
  • 頂点間には辺があることがある。辺は交わらない。
  • 辺で区切られた領域それぞれについて、使用する色がすべて含まれる必要がある。
  • 使う色の上限を求めて、総当りで頂点に割り振って、条件を満たすかチェックして、だめなら使う色を減らして、... で Small くらいは通るんじゃないかと思って実装。
  • が、「全ての頂点とそこから1歩で行ける頂点」内で、使用する色がすべて含まれる必要がある と勘違いしていたことに気づく。
  • 頂点と辺の情報を領域に変換するコードが残り20分で書ければ....
  • 無理でした。

A. FreeCell Statistics

18:13 |  A. FreeCell Statistics - kojingharangの日記 を含むブックマーク はてなブックマーク -  A. FreeCell Statistics - kojingharangの日記  A. FreeCell Statistics - kojingharangの日記 のブックマークコメント

  • N, PD, PG が与えられる。
  • 今日の試合数 <= N、今日の勝率はちょうど PD%、今までの勝率はちょうど PG%。
  • 上記のようなことが有りうるかどうかを判定する問題。
  • 例えば 10 50 100 なら今日の勝率 50% なのに全体の勝率は 100% にならないのでありえないと答える。
  • 今日の試合数 <= N をチェックする。今日の試合数の最小値は、今日の勝率と100の最大公約数で100を割ったものになる。
  • 試行錯誤しつつ、今日の勝率>0 なのに全体の勝率=0 とか極端な場合だけチェックすれば全体の勝率についてはなんとでもなるということが判明
  • コンテスト終了後、Large incorrect であることが判明。
  • Large 通している人のコードで実行した結果と自分のを diff で比べると、N が 10 桁とかでかいときに違っている。
  • Large では N <= 10**15 という条件があったのであった!
  • int N としていたのを long long N としたら一致。
  • くやしけり。
  • 提出前にチェックすべき事をリスト化するなど、ミスを減らす仕組みづくりをしないといけない。

(以下、long long に修正後のソース)

int gcd ( int a, int b )
{
	if(a<b)
	{
		int tmp=a;
		a=b; b=tmp;
	}
	
	int c;
	while ( a != 0 ) {
	c = a; a = b%a;  b = c;
	}
	return b;
}

int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int ans = 1;
		int pd, pg, d=100, g=100, v;
		ll n;
		cin>>n>>pd>>pg;
		//cout<<n<<pd<<pg;
		v = gcd(d, pd);
		pd/=v; d/=v;
		v = gcd(g, pg);
		pg/=v; g/=v;
		//cout<<pd<<" "<<d<<" "<<pg<<" "<<g<<" "<<endl;
		
		//if(g<d)
		//{
		//	int a=d/g + 1;
		//	g*=a; pg*=a;
		//}
		if((pd!=d&&pg==g)||(pd>0&&pg==0) || d>n)
		{
			cout<<"Case #"<<t+1<<": Broken"<<endl;;
			continue;
		}
		//cout<<pd<<" "<<d<<" "<<pg<<" "<<g<<" "<<endl;
		
		cout<<"Case #"<<t+1<<": Possible"<<endl;;
	}
	return 0;
}

B. The Killer Word

18:13 |  B. The Killer Word - kojingharangの日記 を含むブックマーク はてなブックマーク -  B. The Killer Word - kojingharangの日記  B. The Killer Word - kojingharangの日記 のブックマークコメント

  • 文字列 N 個からなる辞書からこっちが1個えらんで、Sean がそれを当てるゲーム。
  • 最初、えらんだ文字列はすべて伏せてある。文字数はわかる。
  • Sean は文字(a-z)をguessする。guessした文字が文字列にあったらそれをオープンにする。なかったら 1pt 減点。
  • Sean のアルゴリズムは、文字の優先順位が与えられるので、その順かつ現時点で有効な選択肢に1つでも含まれる文字ならguessするというもの。
  • その際、そもそも伏せられた文字数から答えが1つに絞れる場合はguessしないで終了するなど、Seanは賢くやるとする。
  • 文字の優先順位が M 個与えられるので、それぞれについて、Sean の減点が一番多くなるような文字列を求める。
  • とりあえずシミュレーションしてみる。
  • Small 通ったので Large やるが計算が終わらない。
  • Large は N <= 10000, M <= 100 で、コードを見るとなにやらループの入れ子が結構深くて M, N, Nのループになってる。
  • 1つ1つ Sean の減点を求めて最大のものを出すとかいって、なんか重複して計算してるものが多そうだから DP みたいなことをやるんだろうけど思いつかず。
  • 解説を見てると、文字数でクラス分けして、さらに1文字guessするごとにオープンされた文字の位置によってさらにクラス分けして、選択肢が1個になるまでシミュレートしていくとよいと書いてある。なるほど.....
  • 今までいろんな問題を見てきて、「なんとかが最大になるようななんとかを求めよ」みたいなときは DP 感がするようになったのでこれをもっと推し進めたく。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int N, M;
		cin>>N>>M;
		string D[N];
		string L[M];
		REP(i, N) cin>>D[i];
		REP(i, M) cin>>L[i];
		//REP(i, N) cout<<D[i]<<endl;
		//REP(i, M) cout<<L[i]<<endl;
		
		cout<<"Case #"<<t+1<<":";
		REP(Li, M)
		{
			int lose[N];
			CLR(lose);
			REP(di, N)
			{
				// choose D[di]
				//cout<<"choose "<<D[di]<<endl;
				int ng[N];
				int nng=0;
				CLR(ng);
				REP(i, N)
				{
					if(D[di].SZ != D[i].SZ) {ng[i]=1;nng++;}
				}
				if(nng==N-1) continue;
				
				// begin guess
				REP(li, 26)
				{
					int contains=0;
					REP(i, N)
					{
						if(ng[i]) continue;
						if(D[i].find(L[Li][li])!=D[i].npos) { contains=1; break; }
					}
					if(contains)
					{
						char guess = L[Li][li];
						//cout<<"guess "<<guess<<endl;
						int revealed=0;
						REP(p, D[di].SZ)
						{
							if(D[di][p]==guess) {revealed=1;break;}
						}
						if(!revealed) lose[di]++;
						REP(i, N)
						{
							if(ng[i]) continue;
							REP(p, D[i].SZ)
							{
								if(D[di][p]==guess && D[i][p]!=guess || D[di][p]!=guess && D[i][p]==guess) {ng[i]=1;nng++;break;}
							}
						}
						if(nng==N-1) break;
					}
				}
			}
			int max = -1;
			int maxi = -1;
			REP(i, N)
			{
				//cout<<lose[i]<<endl;
				if(max<lose[i]) {max=lose[i];maxi=i;}
			}
			cout<<" "<<D[maxi];
		}
		cout<<endl;
	}
	return 0;
}

C. Pseudominion

18:17 |  C. Pseudominion - kojingharangの日記 を含むブックマーク はてなブックマーク -  C. Pseudominion - kojingharangの日記  C. Pseudominion - kojingharangの日記 のブックマークコメント

A, B に手こずったので眺めただけ。

2011-05-08

Google Code Jam 2011 Qualification -> 70pt (通過)

22:59 | Google Code Jam 2011 Qualification -> 70pt (通過) - kojingharangの日記 を含むブックマーク はてなブックマーク - Google Code Jam 2011 Qualification -> 70pt (通過) - kojingharangの日記 Google Code Jam 2011 Qualification -> 70pt (通過) - kojingharangの日記 のブックマークコメント

12121(?)人中3417位で予選通過ということです。次も頑張ります。

A. Bot Trust

22:59 |  A. Bot Trust - kojingharangの日記 を含むブックマーク はてなブックマーク -  A. Bot Trust - kojingharangの日記  A. Bot Trust - kojingharangの日記 のブックマークコメント

  • 廊下があって等間隔に1,2,3...のボタンが置いてある。
  • ロボット2台が廊下の1ボタンのところに置いてある。
  • それぞれのロボットは毎秒ごとに (1) +1方向または−1方向に1マス移動する、(2)移動しない、(3)今いるところのボタンを押す、のいづれかができる。
  • 「ボタンの番号と押すロボットの名前」からなるリストが与えられるので、指定されたロボットが指定された番号のボタンをリストの順に押す。
  • 全部押すまでに何秒かかるか。
  • ロボットが押すべきボタンの位置と、全体として何番目にそのボタンを押すことになるかを各ロボット分のリストに用意する。
  • ロボットがボタンを押せる位置にある かつ ボタン順番値==グローバルなボタンカウンタ ならボタンを押す。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int ans = 0;
		VVI d;
		d.PB(VI());
		d.PB(VI());
		
		int N;
		cin>>N;
		
		REP(i, N)
		{
			char col;
			int loc;
			cin>>col>>loc;
			//cout<<col<<loc<<endl;
			int ci = col=='O'?0:1;
			d[ci].PB(i);
			d[ci].PB(loc);
		}
		//cout<<d<<endl;
		int pi = 0;
		int ci[2];
		int cur[2];
		ci[0]=ci[1]=0;
		cur[0]=cur[1]=1;
		REP(step, 100*100)
		{
			int pushed = 0;
			int end = 1;
			REP(col, 2)
			{
				if(ci[col]>=d[col].size()/2) continue;
				end = 0;
				
				int p = d[col][ci[col]*2+0];
				int loc = d[col][ci[col]*2+1];
				if(cur[col]==loc)
				{
					if(pi==p)
					{
						//cout<<col<<" push "<<cur[col]<<endl;
						pushed=1;
						ci[col]++;
					}
					else
					{
						//cout<<col<<" stay"<<endl;
					}
				}
				else
				{
					int dir = loc - cur[col] > 0 ? 1 : -1;
					cur[col]+=dir;
					//cout<<col<<" move to "<<cur[col]<<endl;
				}
			}
			if(pushed) pi++;
			if(end) break;
			ans++;
		}
		cout<<"Case #"<<t+1<<": "<<ans<<endl;;
	}
	return 0;
}

B. Magicka

22:59 |  B. Magicka - kojingharangの日記 を含むブックマーク はてなブックマーク -  B. Magicka - kojingharangの日記  B. Magicka - kojingharangの日記 のブックマークコメント

  • 変換規則とpushしていく文字リストが与えられる。
  • 1文字ずつリストにpushして、そのたびに変換規則によってリストを変化させるゲーム。
  • リストの最後の2文字→新しい文字に置き換え
  • リストの中に特定の2文字が両方含まれている→リストの中身を消去

(反省点)

  • 変換して変化したらまた再評価するのかとか考えずに実装したらsmallもlargeも通ったけど、そのへん敏感に確認すべきだった。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		vector<string> com;
		vector<string> del;
		string v;
		int nc, nd, nv;
		cin>>nc;
		REP(i, nc)
		{
			string tmp;
			cin>>tmp;
			com.PB(tmp);
		}
		cin>>nd;
		REP(i, nd)
		{
			string tmp;
			cin>>tmp;
			del.PB(tmp);
		}
		cin>>nv;
		cin>>v;
		//cout<<com<<del<<v<<endl;
		vector<char> w;
		REP(i, nv)
		{
			w.PB(v[i]);
			if(w.SZ>=2) REP(j, nc)
			{
				if(w[w.SZ-2]==com[j][0] && w[w.SZ-1]==com[j][1])
				{
					w.pop_back();
					w.pop_back();
					w.PB(com[j][2]);
				}
				if(w[w.SZ-2]==com[j][1] && w[w.SZ-1]==com[j][0])
				{
					w.pop_back();
					w.pop_back();
					w.PB(com[j][2]);
				}
			}
			if(w.SZ>=2) REP(j, nd)
			{
				if(w[w.SZ-1]==del[j][0])
				REP(k, w.SZ-1)
				{
					if(w[k]==del[j][1])
					{
						w.clear();
					}
				}
				if(w[w.SZ-1]==del[j][1])
				REP(k, w.SZ-1)
				{
					if(w[k]==del[j][0])
					{
						w.clear();
					}
				}
			}
		}
		cout<<"Case #"<<t+1<<": "<<w<<endl;;
	}
	return 0;
}

C. Candy Splitting

22:59 |  C. Candy Splitting - kojingharangの日記 を含むブックマーク はてなブックマーク -  C. Candy Splitting - kojingharangの日記  C. Candy Splitting - kojingharangの日記 のブックマークコメント

  • 正の整数値が書きこまれたアメが袋にたくさん入ってる。(なんだそのアメ!!)
  • Seanがアメを2つのグループに分ける。
  • Patrickが2つのグループそれぞれの合計を算出する。合計が等しくないとPatrickは泣き出す。
  • Patrick が泣かなかった場合 Seanは自分がもらうグループを選ぶことができる。
  • Patrick は2進数の加算の繰り上げができないので排他的論理和を計算してしまう。
  • Seanははちゃんと加算できるので、自分で選ぶグループの合計が最大になるように2グループに分けたい。
  • Patrick が泣かないようにできるか、もしそうならSeanが貰える合計の最大値を求める。
  • Patrick が泣かない←→すべてのアメのXOR==0 でとりあえず判断できそう。
  • で、分け方は 2^(アメの数) 通りある。largeではアメの数が最大1000なので全通り試すみたいなのは破綻するな
  • なんかうまい方法があるんだろう
  • (う〜〜〜〜ん.......)
  • (う〜〜〜〜ん.......)
  • (う〜〜〜〜ん.......)
  • 最大にしたいということなので、強欲に「1番小さいのとそれ以外」で分けたらどうだ
  • XORなのでなんかうまくいきそうな気がする
  • 「全XORが0の場合、「任意の1つとその他」でグループ分けするとかならずXOR値が等しくなる」を証明したいでござる
  • 全XOR==0 → 任意の2グループのXOR==0 ということなので、「1番小さいのとそれ以外」で分けても成り立つ。証明だん。
  • なので、全アメの合計から一番小さいやつを引いたのが答え。
  • ソートしたけど、最小値を求めるだけなら不要ですね。。
int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int n;
		cin>>n;
		VI c(n);
		int x=0;
		int sum=0;
		REP(i, n)
		{
			cin>>c[i];
			x ^= c[i];
			sum+=c[i];
		}
		SORT(c);
		sum -= c[0];
		//cout<<c<<endl;
		if(x==0)
		{
			cout<<"Case #"<<t+1<<": "<<sum<<endl;
		}
		else
		{
			cout<<"Case #"<<t+1<<": NO"<<endl;
		}
	}
	return 0;
}

D.

22:59 |  D.  - kojingharangの日記 を含むブックマーク はてなブックマーク -  D.  - kojingharangの日記  D.  - kojingharangの日記 のブックマークコメント

  • 1〜Nのカードが順不同でおいてある。
  • 任意のカード集合の位置を固定したまま机を叩くと、残りのカードが順不同にシャッフルされる。
  • カードを抑える、机をたたく以外のことはできない。
  • カードを昇順にソートしたい。
  • 平均何回机を叩けばソートできるか??ただし誤差は 1-e6 以内とする。
  • なにこれ
  • sample input, output を見ると、2枚のカードを固定しないでシャッフルすると平均2回でソートできるとなっている。ほう。
  • 確率を計算しようと試みる。1*1/2 + 2*1/2*1/2 + 3*1/2*1/2*1/2 + ... と無限に足すと、...わかんないけど 2 に近づくようだ。
  • では、「任意の2枚を入れ替える操作だけができるとして、初期状態から最小何手でソートされた状態になるか?」という問題を解けばいいんじゃね
  • sample output と同じ結果になったので small 提出→incorrect
  • ちょっとバグってたので再提出→incorrect
  • (.....................)
  • 諦める。
  • 問題の解説を見たところ、「正しい位置にないカードだけを1回シャッフルすると平均1枚のカードが正しい位置に移動する」という性質が成り立つようで、
  • 正しい位置にないカードの枚数に ".000000" を付けて出力すれば正解となるらしい。

(反省点)

  • こういう、「サンプルみたいなシンプルなケースで考えてみて、なんか成り立ちそうな性質を思いついて、それをざっくりでもいいから証明してコードを書く」みたいなのが苦手。
  • どう反省すべきか不明だが、もっと賢くなれということかorz

(以下、間違ってますが掲載)

int main()
{
	int T;
	cin>>T;
	//cout<<T<<endl;
	REP(t, T)
	{
		int n;
		int ans=0;
		cin>>n;
		VI d(n);
		REP(i, n)
		{
			cin>>d[i];
		}
		cout<<d<<endl;
		int ans2=0;
		REP(i, n)
		{
			if(i+1!=d[i]) ans2++;
		}
		REP(i, n)
		{
			if(i+1!=d[i])
			{
				int ti=-1;
				FOR(j, i+1, n) { if(d[j]==i+1) { ti=j; break; } }
				swap(d[i], d[ti]);
				ans+=2;
				//cout<<d<<endl;
			}
		}
		//cout<<d<<endl;
		cout<<"Case #"<<t+1<<": "<<ans<<".000000 "<<ans2<<endl;
	}
	return 0;
}

2011-03-19

SRM 500 Div1 250 -> 0(system test failed)

04:09 | SRM 500 Div1 250 -> 0(system test failed) - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 500 Div1 250 -> 0(system test failed) - kojingharangの日記 SRM 500 Div1 250 -> 0(system test failed) - kojingharangの日記 のブックマークコメント

500回記念大会。

  • まず投票できる分を投票して、投票してない人が何人か数える。
  • 投票してない人の分を投票数が少ない順に埋める。
  • 埋め方が1通りでない場合(最低投票数の人数<残り票数のとき)は、その人達の確率に「最低投票数の人数<残り票数」を掛ける
  • 最多票の人たちだけを新たに vulnerable にする
  • 最多票の人数が変わらない時は無限ループになるので 0.0 を返す
  • 最多票が1人のときは決まりなので積み上げてきた確率を返す
  • 500, 1000 問題は開きもせず。
  • で 0 点なのにレートはなぜか 1294 -> 1304 に微増。Div1 のこった のこった !

反省点

  • System Test Results を見ると、1個だけ落ちてる。213, {42, 30, 101, 113, 41, 179, 113, 76, 69, 30, 18, 159, 36}
  • 最大 500 票あるのに、「最小票が何票か」を表す int min_vot の初期値が 100 になっていた (!!
  • 100 を 1000 に変えたらローカルで通った。
  • 確か書いたときは決まっている票が最大 50 個だから 100 でいいだろうと思ってしまったのだった。
  • おしい&ぬるい。。
  • あと結果的には問題なかったが、当初プログラムが想定外に無限ループになってログがでかくなっちゃうのでとりあえず 10 回で打ち切るようにしていたコードを消すの忘れて提出していた。
  • あとデバッグの cout が多いので提出前にコメントアウトすべし。
  • ほんとは、コードを書いてテストが通らなくて「??」てなって cout してみる前に方針ミスなどに気がつけるようになりたいんですが。
  • なんかいろいろもったいなかった。。。。

ソース(追記:要ログイン)

http://www.topcoder.com/stat?c=problem_solution&rm=307554&rd=14429&pm=11342&cr=22919543

追記:上記リンクが要ログインなのでこちらにもソースを貼っておきます。

こちらは最小票カウンタの初期値を 100 から 1000 に変えた版です(w

他の方のソースや解説を見ると、「投票が終わるならば浮動票だけの人が1度でも最多票を獲得することはない」ということに気づくと短いコードになるみたいですね。。

実際書いてて「浮動票onlyの人が最多になるとその時点では確率M/Nだけど、固定票はその時点では確率1なので違っちゃってなんかまずいな」と思っていたのが、浮動票だけの人は結局は選ばれないということで納得です。

class MafiaGame {
	public:
	double probabilityToLose(int N, vector <int> de) {
printf("%s %d\n", __FILE__, __LINE__);
		VI vul(N);
		VI vot(N);
		REP(i, N) vul[i]=1;
		vector<double> pro(N);
		REP(i, N) pro[i] = 1.0;
		int prev_n_vul = -1;
		
		int wdc = 0;
		do
		{
			cout<<"VUL "<<vul<<endl;
			int other = N-de.SZ;
			REP(i, N) vot[i]=0;
			REP(i, de.SZ) if(vul[de[i]]) { vot[de[i]]++; } else { other++; }
			cout<<"VOT "<<vot<<endl;
			cout<<"other "<<other<<endl;
			
			while(other>0)
			{
				cout<<"VO2 "<<vot<<endl;
				int min_vot = 1000;
				int min_cnt = 0;
				REP(i, N) if(vul[i] && vot[i]<min_vot) { min_vot=vot[i]; }
				REP(i, N) if(vul[i] && vot[i]==min_vot) min_cnt++;
				if(min_cnt<=other)
				{
					REP(i, N) if(vul[i] && vot[i]==min_vot) { vot[i]++; other--; }
				}
				else
				{
					// choose other/min_cnt
					double p = (double)other/min_cnt;
					REP(i, N)
					{
						if(other==0) break;
						if(vul[i] && vot[i]==min_vot) { pro[i] *= p; vot[i]++; other--; }
					}
					cout<<"PRO "<<pro<<endl;
				}
			}
			int n_vul = 0;
			int max_vot = 0;
			REP(i, N) if(vul[i] && max_vot < vot[i]) max_vot = vot[i];
			REP(i, N) if(vul[i] && vot[i]==max_vot) n_vul++;
			cout<<"n_vul "<<n_vul<<endl;
			if(n_vul==prev_n_vul) return 0.0;
			prev_n_vul = n_vul;
			REP(i, N) vul[i] = vot[i]==max_vot ? 1 : 0;
			if(n_vul==1) break;
			
			if(wdc++>10) return -1.0;
		}
		while(1);
		
		double ans = 0.0;
		REP(i, N) if(vul[i] && ans<pro[i]) ans = pro[i];
		return ans;
	}
|