夢追い人

"It takes a dreamer to make a dream come true."―Vincent Willem van Gogh

R1A,R1Bオチ

今日R1AとR1B参加してきましたが二落ちでした。。。

結果 A-small A-large B-small B-large C-small C-large score
R1A    o      TL       *       *       *       *      6
R1B    o       o       *       *       *       *      20

Cは参加できないのでコレでGCJ敗退です…Japanか来年頑張ります…
今回はとりあえずR1A-AとR1B-Aを解説したいと思います。多分。

R1A-A

R1Aは全体的に例年より難しかったそうです。
で僕がなんとかsmallを通した問題がコレ。概要は以下。

今日D回ゲームをやった。そして今までに合計でG回ゲームをやっている。
結果は勝ちか負けしかなく、計算機で勝率を見ると今までの勝率Pg%と今日の勝率Pd%が奇跡的にどちらも整数だった。
今現在、DやGの値は忘れてしまったが、自分が今日の間に全部でN回ゲームが出来ることが分かっている。
このときPgやPdの値は正しいものか?それともこの計算機は壊れているか?
※G≧D、D>0、N≧D

とりあえずなんの参考にもならないけど僕のコード

int main() {
	int T; cin >> T;
	for (int t=1; t<T+1; t++) {
		int N, Pd, Pg; cin >> N >> Pd >> Pg;
		int d, g;
		bool flag=false;
		for (d=1; d<=N; d++) {
			int Wd=d*Pd;
			if (Wd%100!=0) continue;
			else break;
			for (g=100; g>d; g--) {
				int Wg=g*Pg;
				if (Wg%100!=0) continue;
				if (g-Wg/100>=d-Wd/100&&Wg/100>=Wd/100) {
					flag = true;
					break;
				} else if (g-Wg/100<d-Wd/100&&Wg/100<Wd/100){
					break;
				}
			}
			if (flag) break;
		}
		if (flag) cout << "Case #" << t << ": Possible" << endl;
		else cout << "Case #" << t << ": Broken" << endl;
	}
}

さて、僕はいわゆる全探索で奇跡的にsmallをとおしてしまったけどコレにはもっといい解法がある。今回はその方法を解説し、largeも通るコードを掲載(とある人のコード拝借m(_ _)m)しようとおもう。

まず僕はしなかったがまずありえない場合をある程度除外できる。
・Pg=0の時、Pdは0でないと定義的にありえない
・Pg=100の時、Pdは100でないと定義的にありえない
そして、ここまで場合分けした後でそれ以外について次が言える

(D×Pd) mod 100 = 0

であるから、Dが最小の時だけ考えればいい。Pdと100の最小公倍数をPdで割った値をここでDとすると(D×Pd)がPdと100の最小公倍数となりこの等式が成立する。そしてこの時のDは最小のはず(∵最小公倍数以下のPdと100の公倍数はない)
現実的にはなるべく手間は省きたいので100をPdと100の最大公約数でわって先ほどと同じDの値を求めることになる。これは最大公約数をgcdなどとしてPdと100に関する等式、そして最小公倍数との式を導けば自明であると思う。

そしてこの時このDがNより大きいとおかしいということになる。結果次のように書けばいい。

int main(){
    int T;
    scanf("%d",&T);
    for(int ix=1;ix<=T;ix++){
        long long N,PD,PG;
        scanf("%lld%lld%lld",&N,&PD,&PG);
        bool pos;
        if(PG==0){
            if(PD==0) pos=true;
            else pos=false;
        }else if(PG==100){
            if(PD!=100) pos=false;
            else pos=true;
        }else{
            if((long long)(100/__gcd((long long)100,PD))<=N) pos=true;
            else pos=false;
        }
        if(pos) printf("Case #%d: Possible\n",ix);
        else printf("Case #%d: Broken\n",ix);
    }
}

R1B-A

これは自力でとけたので他人のコードを拝借するような真似は(ry

問題の概要はコチラ

あるゲームの各チームのレート(RPI)を次のように定義した。
 RPI=0.25*WP+0.5*OWP+0.25*OOWP
この時各々の値は以下のように定める。
・WPはそのチームの全体の勝率
・OWPはあるチームをAとおくと、そのAと戦ったチームのAとの戦績を除いたWPの平均
・OOWPはそのチームと戦ったチームのOWPの平均
試合結果からRPIを計算せよ。

ほぼ読解力の問題。OWPの概念が難しい。
ともかく読解すると後は意外に単純な実装でいける。

というわけで答えは以下。

int main() {
	int T; cin >> T;
	for (int ix=1; ix<=T; ix++) {
		int N; cin >> N;
		ves sc(N);
		for (int i=0; i<N; i++)	cin >> sc[i];
		vector<double> team(N, 0.0), owp(N);
		for (int i=0; i<N; i++) {
			double won=0, game=0, awp=0.0;
			int c=0;
			for (int j=0; j<N; j++) {
				if (sc[i][j] == '1') won++;
				else if (sc[i][j] == '.') continue;
				double w=0, g=0;
				for (int k=0; k<N; k++) {
					if (k==i) continue;
					else if (sc[j][k]=='1') w++;
					else if (sc[j][k]=='.') continue;
					g++;
				}
				c++;
				awp += w/g;
				game++;
			}
			owp[i] = awp/c;
			team[i] += won/game*0.25+awp/c*0.5;
		}
		printf("Case #%d:\n",ix);
		for (int i=0; i<N; i++) {
			double aowp=0;
			int c=0;
			for (int j=0; j<N; j++) {
				if (sc[i][j]=='.') continue;
				aowp += owp[j];
				c++;
			}
			team[i] += aowp/c*0.25;
			printf("%g\n", team[i]);
		}
	}
}

全体を通じて

QRが簡単だったので油断していた。

やはりまだまだ精進が足りないようだ。今回は某携帯会社の言葉をかりるとGoogleに“ちやほや”されたのかも…笑笑
とりあえず試験が終わり次第蟻本とAOJとPKUとTopCoderで精進精進w


GCJJではTシャツもらえるように頑張ります。

p.s. 時間があったらR1B-Bあたりは解説記事書くかも?それでは!