Topcoderの最近のブログ記事

Topcoderの比較グラフを作ったよ!!


FlexのLineChartとかで背景色を設定するのは難しかったから、

前のやつは、xml埋め込みで静的なデータを表示してたんだけど、ちょっと頑張って自動更新してみた。



忘れないうちに、

・FlashVarsでhtmlから変数を受けとるとか

・LineChartで背景色を範囲を指定して設定するとか

はメモしとくよ!!



頑張って"ブログに書く"ということを忘れないようにする!!





んで、せっかく作ったから、ハチロク世代のトップコーダー部のページとか、

誰かのブログにも貼れるようにした(つもりだよ!!)


常識的なアクセス量なら直リンでokだよ!!





FlashVarsのところを、表示したいcoderのuser_idに書き換えてね!!

複数表示する時は、","で区切ってね。スペースとか入れるとうまく表示されないかもよ

widthとかheightも書き換えるといいよ!!



貼付けた結果は、一個前の記事にあるよ



はてなに貼る場合は、id:nitoyonさんのガジェットを使ってね!!

ここにいって、URLのところに
http://labs.isocchi.com/topcoder/TopcoderGraph.swf?users=22727085,22712423

とかってコピペするといいよ!!usersは表示したいuserの(ry


while(true) nitoyon++;




コメント欄に、もっとこういう風にしろよとか書くと、

週末とかに反映されてるかもね。


だから、直リンでブログとかに貼ってるといつの間にかグラフが賑やかになってるかも

うちでハチロク世代のTopCoder部で、カレーを食べてトップコーダーをやる会をいました。

参加者は、
isocchi, misho, suztomo (順番はぽにょにならってabc順)


 詳しくは、suztomoの日記で



んで、この前作った、topcoderのグラフを自動で更新するようにしました。


なんか、最近みんな下がってるね


外部ブログに貼ることもできる・・・と思う(たぶん)


詳しくはあとで書くよ!!



 








書くの忘れてた!!

日曜日にあったSRM 422 DIV2でやっとrateが4桁になりました。

ピクチャ 1.png



250点問題(MultiNumber)
1236とか与えられたら、それを前・後ろに2つに分割して(123, 6)、
それぞれについて、1桁ずつかけ算した結果(1*2*3, 6)が、前半後半で等しければ"YES"、違えば"NO"と返すだけの簡単なお仕事。


・500点問題(PrimeSoccer)
5分間にチームAが得点する確率をskillOfTeamAパーセント。Bチームが得点するか確率をskillOfTeamBパーセント。それぞれは互いに独立。
90分間の試合で、少なくとも1チームの得点結果が素数になる確率を求める。

最初問題文読んでて、prime numberが素数と分からず。重要な数字になればいいのはわかったけど重要な数字っていくつだよ!!とか思ってた笑

id:nishiohirokazuさんのiVocaで「TopCoderに出てくる英単語」を学習できるようにしました
に載ってる単語は読めるようにしとこう。

フツーに各チームずつ素数の点を取る確率を計算すればいいんだけど、
最初、チームAが素数点を取る確率とチームBが素数点を取る確率を単純に足してて、
少なくともだから、素数にならないの余事象をとらなきゃいけないのをド忘れしてたりした。

いつもにくらべれば簡単だった気がするー。


public class PrimeSoccer {
	final int[] PRIMES = {2, 3, 5, 7, 11, 13, 17};
	
	public double getProbability(int skillOfTeamA, int skillOfTeamB) {
		double probA = 0.0;
		double probB = 0.0;
		double prob = 0.0;
		
		for(int i=0; i%lt;PRIMES.length; i++) {
			probA += getProb(skillOfTeamA, PRIMES[i]);
		}
		for(int i=0; i%lt;PRIMES.length; i++) {
			probB += getProb(skillOfTeamB, PRIMES[i]);
		}
		
		prob = 1-(1-probA)*(1-probB);
		
		return prob;
	}
	
	/** skill%の確率で得点するとき、point点とる確率を返す */
	private double getProb(int skill, int point) {
		double skillPercent = skill/100.0;
		double rskillPrcent = (100-skill)/100.0;
		double a = 1;
		int i;

		for(i=0; i<point; i++) {
			a*=skillPercent;
		}
		for( ; i<18; i++) {
			a*=rskillPrcent;
		}
		return a*combination(18, point);
	}
	
	/** nCrを返す */
	private int combination(int n, int r) {
		int i,p=1;
		
		for(i=1; i<=r; ++i) {
			p *= (n-i+1);
			p /= i;
		}
		
		return p;
	}
	
}


1000点問題(BirthdayCake)
intだと桁が足りないのでlongを多用するんだけど、キャスト忘れがひどかった。
doubleを使う時は例えば
  double a = 3/2;
とかってやると、 int/intは小数点以下切り捨てでintになるから、aには1が入っちゃうから、どっちかを事前にキャストして、
  double a = (double)3/2;
とかにするんだけど、

longを使うのは久々だったから完全に忘れてた。
50桁のビットフラグを使いたいから、1をシフトさせてってやるんだけど、
1はint型だから、1シフトさせた数(1 << 50)もint型になっちゃう。

正しくするには、1L << 50とかってしなきゃダメ!!

javaでは、
1, 2, 3 → int型
1.2 3.14 → float型
1.0d 5.4d → double型
1L 1000L → long型
になる。


んで、ループの仕方で、最初はフルーツの全ての組合せについて調べてたら、
Exampleでtimeアウトになってしまい、問題文の、
fruiteは最大50個、friendは最大20人ってあるので、

調べなきゃいけないフルーツの組合せは、最大50C25 > 10^14で、
全ての人の組合せは、2^20 > 10^6で
明らかに後者の方が少なくって、

実際にプログラム書いてみたら、後者の方は最大10^6だけど、実際は途中でbreakしたりreturnしたりでフツーにtimeoutにならなかった!!


combinationはよく使うかもだからもっと高速化した方がいいかも。
昔課題でやった気がする。

あと、順番に調べていくやつで、6個中3個とる場合だと、
000111 -> 001011 -> 010011 -> 100011 -> 001101 -> 010101 -> 100101みたいな感じで調べて行ったんだけど、もっといい方法あるのかな??

あんま探索けいは得意じゃないんだよね汗


import java.util.HashMap;

public class BirthdayCake {

	public int howManyFriends(String[] availableFruits, String[] friendsDislikings, int K) {
		// その人が食べれるものを1としたlong型のビットフラグzの配列
		long[] availableTable = new long[friendsDislikings.length];

		// 全部1で初期化
		long allAvaiable = (1L << availableFruits.length) - 1;
		for(int i=0; i<availableTable.length; i++) {
			availableTable[i] = allAvaiable;
		}

		// フルーツ名(String)と桁(int)の対応を設定
		HashMap<String, Integer> fruitsMap = new HashMap<String, Integer>();
		for(int i=0; i<availableFruits.length; i++) {
			fruitsMap.put(availableFruits[i], i);
		}

		// 食べれないものを0にしていく
		for(int i=0; i<friendsDislikings.length; i++) {
			String[] dislikes = friendsDislikings[i].split(" ");
			for(int j=0; j<dislikes.length; j++) {
				if(!fruitsMap.containsKey(dislikes[j])) continue;
				int index = fruitsMap.get(dislikes[j]);
				availableTable[i] -= 1L << index;
			}
		}

		
		int max = 0;

		// 最大の人数を返せばよく、1人から順に調べる。
		for(int n=1; n<=friendsDislikings.length; n++) {
			int[] choice = new int[n];
			for(int i=0; i<choice.length; i++) {
				choice[i] = i;
			}
			
			int loop = combination(availableTable.length, n);
			for(int count=0; count<loop; count++) {

				// n人の食べれるものをANDを使って取得。
				long temp = availableTable[choice[0]];
				for(int i=1; i<choice.length; i++) {
					temp = temp & availableTable[choice[i]];
				}
				
				// K個以上食べれればok
				if(Long.bitCount(temp) >= K) {
					max=n;
					// n人はokと分かったのでbreakしてn+1人を調べる。
					break;
				}
					
	
				if(count==loop) {
					// n人でダメなのでn+1がokなはずがなく、ここで終了。
					return max;
				}
				
				// n人の取り方を設定する。
				if(choice[choice.length-1] == friendsDislikings.length-1) {
					int cnt;
					for(cnt=1; cnt<choice.length; cnt++) {
						if(choice[choice.length-1-cnt] != friendsDislikings.length-1-cnt) {
							choice[choice.length-1-cnt]++;
							break;
						}
					}
					for(int j=choice.length-1-cnt; j<choice.length-1; j++) {
						choice[j+1] = choice[j]+1;
					}
	
				} else {
					choice[choice.length-1]++;
				}
					
			}
		}
		return max;		
	}

	private int combination(int n, int r) {
		int i;int p=1;
		
		for(i=1; i<=r; ++i) {
			p *= (n-i+1);
			p /= i;
		}
		
		return p;
	}
}

・500点問題
1直線上に質量の与えられた物質がN個置かれていて、さらにもう1つ置く時に、万有引力的につり合う位置はdoubleでN-1個返せって問題。

高校の物理とかで、問題が言ってることは理解できるんだけど、
フツーにやったら高次方程式になりそうで、プログラムで解き方が分からなくて断念。

→2分探索で解けるよと教えてもらったのでやってみた。



まず、x[n]とx[n+1]の間に一個あるんだから、とりあえず真ん中をとって計算してみて、
左の方が大きければ求めたい点は右半分にあり・・・ってなって、
それを再帰でぐるぐるまわして行くと、範囲が半分の半分の半分の・・・ってなって最終的に誤差が1e-9になれば良くって、
誤差が1e-9ってめっちゃ精度高いように見えるけど、2^30が10^9くらいだから30回くらいやれば答えは出て、1回のループが意外と思い計算じゃないのでタイムアウトにもならないみたい。


・1000点問題
コンテスト終了力後に解き終わって、あとでシステムテストしてみたら通ってた涙

問題は、
	"###...#.###.###.#.#.###.###.###.###.###",
	"#.#...#...#...#.#.#.#...#.....#.#.#.#.#",
	"#.#...#.###.###.###.###.###...#.###.###",
	"#.#...#.#.....#...#...#.#.#...#.#.#...#",
	"###...#.###.###...#.###.###...#.###.###"
みたいな感じでデジタル表示で数字を表していて、いくつか電球が切れた(?)ものが与えられて、もともと表示していた可能性のある数字の平均を返す問題。




とりあえず方針として、
String配列はややこしいので、1桁ずつのデジタル数字に直す。
切れた電球に何かを加えるともとの表示になるので、つまり、今すでに光っている部分を光らせない数字の補集合をとれば良い。
簡単にするのに光ってるのを1、消えてるのを0にして、
事前に用意した1とか2とかのとANDをとって、変化がなければおkってこと。


1000点問題始めて解けた。時間外だけどww


せっかくなので復習。


・250点問題
[問題文]
あなたは始めmyUnits人の兵士を持っており、各兵士は毎ターンやぐらに1のダメージを与える。相手は兵士を持っておらず、その代わり、ライフがhpTのやぐらをnumT持っている。各やぐらは、毎ターンattackT人の兵士を倒す。

戦闘の流れは、
1. あなたの兵士が、任意のやぐらに1pointずつダメージを与える。
2. 相手が、残っているやぐらの数*attackTの数のあなたの兵士倒す。

相手を倒すのに必要な最小のターン数を返しなさい。ただし、倒せない場合は-1を返す。


[定義]
Class: Towers
Method: attack
Parameters: int, int, int, int
Returns: int
Method signature: int attack(int myUnits, int hpT, int attackT, int numT)


[制約]
・myUnitは、1以上1000000以下
・hpT, attackT, numTは、1以上10000以下


[方針]
普通に片っ端からやぐらを倒していく。


[ソース]
public class Towers {
	
	public int attack(int myUnits, int hpT, int attackT, int numT) {
		// 現在のターン
		int turn = 0;
		// 攻撃中のtowerの残りhp
		int hp = hpT;
		
		while(numT > 0) {
			// 全滅したらゲームオーバー
			if(myUnits <= 0) return -1;
			turn++;

			// 自分のターン
			hp -= myUnits;
			while(hp <= 0) {
				hp += hpT;
				numT--;
			}
			
			// 相手のターン
			myUnits -= numT * attackT;
			
		}
		
		return turn;
	}
}


・500点問題
[問題文]
1からnまでの数字の中からm個の数字を選ぶ。同様に、ランダムに選ばれたm個の数字と少なくともk個が一致していればあなたの勝ちとする。
n, m, kが与えられたときあなたの勝つ確率をdouble型で返しなさい。


[定義]
Class: TwoLotteryGames
Method: getHigherChanceGame
Parameters: int, int, int
Returns: double
Method signature: double getHigherChanceGame(int n, int m, int k)


[制約]
・nは、2以上 8以下
・mは、1以上 n-1以下
・kは、1以上 m以下


[方針]
高校の確率でやる計算をプログラムでさせる。
ランダムとか一致とかはどうでもよくて、数A風に、
「(n-m)個の白玉とm個の赤玉があります。玉をm個取り出す時に少なくともk個が赤玉である確率を求めよ」と解釈。

少なくともk個一致すればいいのだから、
「1つも一致しない確率 + 1つ一致する確率 + ・・・ + (k-1)個一致する確率」の余事象でok

コンビネーションの求め方は再帰の方が良さげだけど、n, m, kがmax 8なので
これでもオーバーフローしなかった。

(変数名とかメソッド名がわやなのはスルーな方向で)


[ソース]
public class TwoLotteryGames {
	
	public double getHigherChanceGame(int n, int m, int k) {
		int bunshi = 0;
		int bunbo = 1;
		
		// 勝つ場合の数を求める
		for(int i=0; i<k; i++) {
			// i(<k)個一致する確率
			bunshi += getA(n, m, i);
		}
		
		// 全事象を求める
		for(int i=n; i>(n-m); i--) {
			bunbo *= i;
		}
		
		return 1-(double)bunshi/bunbo;
		
	}
	
	/** n個中m個が当たりで、それをm個引きi個当たる当て方を求める */
	private int getA(int n, int m, int i) {
		int result = 1;
		
		// はずれを(m-i)個ひく
		for(int j=(n-m); j>(n-m-m+i); j--) {
			result *= j;
		}
		
		// あたりをi個ひく
		for(int j=0; j<i; j++) {
			result *= (m-j);
		}
		
		// 何番目で当たりをひくかで、mCi通りあるのでそれを掛ける
		result *= combination(m,i);
		
		return result;
	}
	
	/** nCrで表記される組合せを返す。 */
	private int combination(int n, int r) {
		int i,p=1;
		
		for(i=1; i<=r; ++i) {
			p *= (n-i+1);
			p /= i;
		}
		
		return p;
	}
}




・1000点問題
[問題文]
あなたは始めmyUnits人の兵士を持っており、相手は、0人の兵士とライフがbarHpの兵舎を持っている。各兵士の攻撃力は1で、 ライフも1である。相手は始め兵士を持っていないがターン終了時に兵舎が倒されていなければ、兵舎unitsPerRound人の兵士が追加される。兵舎のライフはbarHpである。

戦闘の流れは、
1. あなたの兵士が、相手兵士か兵舎を任意に1pointずつダメージを与える。
2. 相手がの兵士が、その数だけあなたの兵士を倒す。
3. 相手の兵舎が倒されていなければ、相手にunitsPerRound人の兵士が追加される。

相手を倒すのに必要な最小のターン数を返しなさい。ただし、倒せない場合は-1を返す。


[定義]
Class: BarracksEasy
Method: attack
Parameters: int, int, int
Returns: int
Method signature: int attack(int myUnits, int barHp, int unitsPerRound)


[制約]
・myUnits, barHp, unitsPerRoundは1以上 50以下


[方針]
250と違って、兵士と兵舎のどっちを優先して攻撃するかが問題。
myUnitsは増えることはないので、基本的にはそれを減らさないように兵士を優先して倒し、兵士を全滅させたら余力で少しずつ兵舎のライフを削っていく感じ。

ただ、これだけだと、exampleは通るがSystemテストで落ちる。理由は、最小のターン数でなはいから。

例えば、
myUnits:22, barHp:48, unitsPerRound: 21の場合、

最初のターンで、兵舎に22のダメージを与えると、その後は1ずつしか減らせない。

初期:myUnits:22, barHp:48, numT:0
1. myUnits:22, barHp:26(-22), numT:21(+21)
2. myUnits:22, barHp:25(-1), numT:21(-21+21)
3. myUnits:22, barHp:24(-1), numT:21(-21+21)
4. myUnits:22, barHp:23(-1), numT:21(-21+21)
      ・
      ・
      ・
13. myUnits:22, barHp:14(-1), numT:21(-21+21)
最小のパターンはここで一斉攻撃
14. myUnits:9, barHp:0(-14), numT:13(-8)
15. myUnits:5, barHp:0, numT:4(-9)
16. myUnits:5, barHp:0, numT:-1(-5)

これを判定しなきゃだめっぽくて、

でも、このフラグは、barHpがmyUnits以下になった時(一斉攻撃すれば倒せる状況になった時)に、
兵舎を一斉攻撃し、numTがあまり減らせなくても全滅せずに倒せるかどうかを調べれば良いので、そういう処理をすればok


[ソース]
public class BarracksEasy {

	public int attack(int myUnits, int barHp, int unitsPerRound) {
		int turn = 0;
		int numT = 0;
		
		while(numT > 0 || barHp > 0) {
			if(myUnits <= 0) return -1;

			// 敵ソルジャーを先に攻撃
			if(unitsPerRound < myUnits && 
					!(
							// 全力でbarを攻撃すればこのターンを倒せる
							myUnits >= barHp
							// barを倒したは良いが、ソルジャーに攻撃されて全滅するということはない
							&& shuoldKillBar(myUnits - (numT-(myUnits-barHp)), numT-(myUnits-barHp))
					)
			) {

				// 敵ソルジャーを先に攻撃
				numT -= myUnits;
				if(numT < 0) {
					barHp += numT;
					numT = 0;
				}
			// barを先に攻撃
			} else {
				barHp -= myUnits;
				if(barHp<0) {
					numT += barHp;
					barHp = 0;
				}
			}
			
			// 相手のターン
			myUnits -= numT;
			
			if(barHp > 0) numT += unitsPerRound;
			turn++;
		}
		
		return turn;
	}
	
	/** Barを一斉攻撃して倒した場合、その後全滅させられないか */
	private boolean shuoldKillBar(int myUnits, int numT) {
		while(numT > 0) {
			numT -= myUnits;
			if(numT <= 0) return true;

			myUnits -= numT;
			if(myUnits <= 0) return false;
		}
		
		return false;
	}
}


iKnow

あわせて読みたいブログパーツ
Firefox meter