Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2013-10-06

SRM 593 Div1 250 HexagonalBoard

18:08 |  SRM 593 Div1 250 HexagonalBoard - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 593 Div1 250 HexagonalBoard - kojingharangの日記  SRM 593 Div1 250 HexagonalBoard - kojingharangの日記 のブックマークコメント

  • 縦横 N 個の蜂の巣状のマスがあり、いくつかに印がついている。隣り合った印は違う色で塗りたい。最低何色必要か求めよ。
  • 1≦N≦50
  • 左上から色を決めていく(間違い)
  • 各マスの色は、その左、左上、右上に使われている色のどれとも違う色にする
  • 提出
  • 嫌な予感がする
  • U 字状に印が繋がってた場合、一番下で合流するまで上端右側の色は決められないのでだめだ
  • これはあかんやつや
  • wikipediaとかで調査。どうも一般グラフの効率的な彩色アルゴリズムはないっぽい
  • 蜂の巣状というのがヒントかも
  • 全部に印がある場合、3色で塗れる
1 2 3 1 2 3
 3 1 2 3 1 2
  2 3 1 2 3 1
   ....
  • というわけで答えは0,1,2,3色のどれかなので泥臭く決めるのだろう
  • 印がなければ0色
  • edgeがなければ1色
  • 2色で済む場合は、DFSして交互に決めていけば色は自動的に決まる。
  • 具体的には、グラフが森の場合と、閉路はあるんだけどすでに訪れたとこの色が自動的に決まる色と一致している場合。
// 左上の 1 から DFS 開始
- 1 ?
 2 - ?
  1 ? -
↓ 1に戻ってきたけど2の隣なので1で良かった、結果オーライ!的な
- 1 2
 2 - 1
  1 2 -
  • 戻ってきた時に第3の色が必要なら return 3, そういうことが無かった場合は使った色数を返す。
  • ていうのを実装。なんかコードが汚い。75.97点。
  • accepted

vector <string> B;
VVI vis;
ll ans;
int N;
class HexagonalBoard {
	public:
	void dfs(int x, int y, int col) {
		if(vis[y][x]) {
			if(vis[y][x]!=col) {ans=3;return;}
			return;
		}
		vis[y][x]=col;
		int dx[] = {-1,0,1,1,0,-1};
		int dy[] = {0,-1,-1,0,1,1};
		REP(i, 6) {
			int nx=x+dx[i];
			int ny=y+dy[i];
			if(0<=nx&&nx<N && 0<=ny&&ny<N && B[ny][nx]=='X') dfs(nx, ny, col==1?2:1);
		}
	}
	
	int minColors(vector <string> BB) {
		B=BB;
		N=B.size();
		vis = VVI(N, VI(N));
		ans = 0;
		REP(y, N) REP(x, N) {
			if(!vis[y][x] && B[y][x]=='X') dfs(x, y, 1);
			if(ans==3) return 3;
		}
		REP(y, N) REP(x, N) {
			ans=max(ans, vis[y][x]);
		}
		//cout<<w<<endl;
		return ans;
	}
};



SRM 593 Div1 450 MayTheBestPetWin

18:09 |  SRM 593 Div1 450 MayTheBestPetWin - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 593 Div1 450 MayTheBestPetWin - kojingharangの日記  SRM 593 Div1 450 MayTheBestPetWin - kojingharangの日記 のブックマークコメント

  • N匹の動物たちが2チームに分かれてリレー競走をする。動物 i は A[i]〜B[i] の時間で走る。
  • チーム分けが決まると、チームの合計タイムの差としてありうる値の最大値が決まる。その値が最小になるようにチーム分けしたとき、その最小値を求めよ。
  • 2≦N≦50, 1≦A[i], B[i]≦10,000
  • わからん B[i]-A[i] すると何かに使えるか?
  • ...
  • 終了
  • @nico_shindanninさんの生放送を見る. \オススメですよ/(宣伝)
  • Sum[i] = A[i]+B[i] と置くと、答えはうまく Sum[i] だけで表せるようだ
  • |S fast - T slow| = |ΣA[s] - ΣB[t]| = |(SumA - ΣA[t]) - ΣB[t]| = |SumA - ΣSum[t]| (s in S, t in T)
  • |S slow - T fast| = |ΣB[s] - ΣA[t]| = |(SumB - ΣB[t]) - ΣA[t]| = |SumB - ΣSum[t]| (s in S, t in T)
  • おおおおお
  • ↓あとで(accepted in practice room)
class MayTheBestPetWin {
	public:
	int calc(vector <int> A, vector <int> B) {
		int N = A.size();
		VI SUM(N);
		REP(i, N) SUM[i]=A[i]+B[i];
		int sumA = accumulate(ALL(A), 0);
		int sumB = accumulate(ALL(B), 0);
		int maxK = 10000 * N * 2 + 500;
		VVI dp(2, VI(maxK));
		int men=0;
		dp[men][0] = 1;
		REP(i, N) {
			REP(k, maxK) {
				if(dp[men][k]==0) continue;
				dp[1-men][k] = 1;
				dp[1-men][k + SUM[i]] = 1;
				assert(k + SUM[i] < maxK);
			}
			men ^= 1;
		}
		int ans = 1<<30;
		REP(k, maxK) {
			if(dp[men][k]) ans = min(ans, max(abs(sumA-k), abs(sumB-k)));
		}
		return ans;
	}
};

2013-08-31

Typical DP contest D サイコロ

01:54 |  Typical DP contest D サイコロ - kojingharangの日記 を含むブックマーク はてなブックマーク -  Typical DP contest D サイコロ - kojingharangの日記  Typical DP contest D サイコロ - kojingharangの日記 のブックマークコメント

  • サイコロを N 回振ったとき、出た目の積が D の倍数となる確率を求めよ
  • 1≦N≦100, 1≦D≦10^18
  • 倍数ということで素因数分解した形で考える
  • サイコロの目 1〜6 は全て 2^i * 3^j * 5^k の形で表せる。
  • だからなんなのさ
  • シャワー浴びてたらひらめく !
  • D に 7 以上の素因数があったらサイコロを何回振っても D の倍数にはならない ... ので 0 を印字して終わる
  • D = 2^p2 * 3^p3 * 5^p5 と表せたので、なんか途中の状態を 2^? * 3^? * 5^? と表しておくんじゃないか
  • dp[2の指数][3の指数][5の指数] = n回目にそういう状態になる確率
  • 初期値は dp[0][0][0] = 1. 他の要素は 0
  • サイを降るごとに状態全てについて目で場合分けして確率を更新. 3が出たら 2^0 * 3^1 * 5^0 掛けるので [i][j+1][k]のとこを更新 みたいな。
  • 最終的に i ≧ p2 && j ≧ p3 && k ≧ p5 の確率を全部たしたものが答え。
  • TLE
  • コンテスト終了

(あとで)

  • DP表の無駄なコピーを消す && dp[i][j][k]==0 ならスキップ
  • accepted in practice

double dp[2][210][110][110];

int main() {
	//ios::sync_with_stdio(false);
	ll N, D;
	int tb[]={
		0, 0, 0,
		1, 0, 0,
		0, 1, 0,
		2, 0, 0,
		0, 0, 1,
		1, 1, 0,
	};
	while(cin>>N>>D) {
		int p2=0, p3=0, p5=0;
		while(D%2==0) p2++,D/=2;
		while(D%3==0) p3++,D/=3;
		while(D%5==0) p5++,D/=5;
		if(D>1) {
			cout<<0<<endl;
			continue;
		}
//		cout<<p2<<" "<<p3<<" "<<p5<<endl;
		int men=0;
		REP(m, 2) REP(i, 210) REP(j, 110) REP(k, 110) dp[m][i][j][k]=0;
		dp[men][0][0][0] = 1;
		REP(I, N) {
			REP(i, 210) REP(j, 110) REP(k, 110) {
				if(dp[men][i][j][k]==0.0) continue;
				REP(me, 6) {
					int ni = i+tb[me*3+0];
					int nj = j+tb[me*3+1];
					int nk = k+tb[me*3+2];
					if(ni<210 && nj<110 && nk<110) dp[1-men][ni][nj][nk] += dp[men][i][j][k] / 6.0;
				}
			}
			REP(i, 210) REP(j, 110) REP(k, 110) dp[men][i][j][k]=0;
			men ^= 1;
//			PRINT3(dp, 5, 5, 1);
		}
		double ans = 0;
		REP(i, 210) REP(j, 110) REP(k, 110) if(i>=p2 && j>=p3 && k>=p5) ans += dp[men][i][j][k];
		
		cout<<ans<<endl;
	}
	
	return 0;
}



Typical DP contest C トーナメント

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

  • 2^K 人がトーナメント対戦する。人 i, j が対戦して i が勝つ確率が式で与えられる。各人が優勝する確率を求める。
  • 1≦K≦10
  • わからないので工夫とかなしで何を計算すべきか考えてみる
  • dp[k][i] ... k回戦が終わった段階で人iが勝ち残ってる確率 と置いてみる. (実際は2面だけ用意した
  • 各対戦で戦う可能性のある人すべてのパターンで確率を計算してdp[k+1]に足してく
  • 決勝は 512x512 →おー間に合うじゃん
  • accepted

double f(double EP, double EQ) {
	return 1.0 / (1 + pow(10, (EQ-EP)/400));
}

int main() {
	//ios::sync_with_stdio(false);
	ll K;
	while(cin>>K) {
		VI E(1<<K);
		REP(i, 1<<K) cin>>E[i];
		
		vector<double> p(1<<K, 1), np(1<<K);
		REP(round, K) {
			int size = 1<<round;
			int games = (1<<K)/size/2;
//			cout<<"round: "<<size<<" "<<games<<endl;
			REP(game, games) {
				int asidx = game*size*2;
				int bsidx = game*size*2+size;
				REP(ai, size) {
					REP(bi, size) {
						double pawin = f(E[asidx+ai], E[bsidx+bi]);
						np[asidx+ai] += p[asidx+ai]*p[bsidx+bi]*pawin;
						np[bsidx+bi] += p[asidx+ai]*p[bsidx+bi]*(1-pawin);
					}
				}
			}
//			cout<<"np: "<<np<<endl;
			p = np;
			np = vector<double>(1<<K);
		}
		REP(i, 1<<K) cout<<p[i]<<endl;
	}
	
	return 0;
}

2013-08-27

SRM 589 Div1 250 GooseTattarrattatDiv1

22:33 |  SRM 589 Div1 250 GooseTattarrattatDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 589 Div1 250 GooseTattarrattatDiv1 - kojingharangの日記  SRM 589 Div1 250 GooseTattarrattatDiv1 - kojingharangの日記 のブックマークコメント

  • 文字列が与えられる。文字の置換を文字列全部に対して何回か行う。文字列が回文になるように置換するとき、置換する文字数の最小値を求める問題。
  • 1≦N≦50
  • 何と何と何を同じ文字にすると良いかを union-find で求める。geese なら g-e, e-s ということで g-e-s を同じ文字にすればいい。
  • それぞれのグループについて、どの文字に統一するかを決める。文字列における文字の出現回数が多いものに合わせるとコスト最小。
  • accepted
struct UnionFind {
	vector<int> data;
	UnionFind(int size) : data(size, -1) { }
	bool Union(int x, int y) {
		x = root(x); y = root(y);
		if (x != y) {
			if (data[y] < data[x]) swap(x, y);
			data[x] += data[y]; data[y] = x;
		}
		return x != y;
	}
	bool Find(int x, int y) { return root(x) == root(y); }
	int root(int x) { return data[x] < 0 ? x : data[x] = root(data[x]); }
	int size(int x) { return -data[root(x)]; }
};

class GooseTattarrattatDiv1 {
	public:
	int getmin(string S) {
		UnionFind uf(26);
		int N=S.size();
		REP(i, N/2) {
			uf.Union(S[i]-'a', S[N-1-i]-'a');
		}
		map<int, int> vis;
		int ans = 0;
		REP(i, 26) {
			int root = uf.root(i);
			if(vis[root]) continue;
			vis[root]=1;
			string same;
			VI cnt;
			REP(j, 26) {
				if(root==uf.root(j)) {
					same+=string(1, 'a'+j);
					int ct=0;
					REP(k, N) if(S[k]=='a'+j) ct++;
					cnt.PB(ct);
				}
			}
			//cout<<string(1, 'a'+i)<<" "<<same<<" "<<cnt<<endl;
			if(same.size()==0) continue;
			sort(ALLR(cnt));
			ans += accumulate(ALL(cnt), 0)-cnt[0];
		}
		return ans;
	}
};

2013-08-14

SRM 588 Div1 250 GUMIAndSongsDiv1

09:42 |  SRM 588 Div1 250 GUMIAndSongsDiv1 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 588 Div1 250 GUMIAndSongsDiv1 - kojingharangの日記  SRM 588 Div1 250 GUMIAndSongsDiv1 - kojingharangの日記 のブックマークコメント

  • 歌の長さD[i]、歌の音の高さtone[i]が与えられる。歌と歌の間にはtoneの差だけ時間をおかないといけない。
  • 時間T以内で沢山歌いたい。何曲歌えるか。
  • 1≦N≦50, 1≦D[i]≦100,000, 1≦tone[i]≦100,000, 1≦T≦10,000,000
  • 歌の長さでソート(間違い)
  • dp[i][j][n] ... 歌iまで考慮したときに、最後に歌ったのが歌jで、歌った曲数がnの時の最小時間
  • で、最後に表を見て最小時間が T 以内のもので最大の n が答え
  • という方針。
  • 最後のサンプルが合わない

(あとで)

  • tone でソートしておくらしい。
  • DPでは直前の歌と今の歌から最小時間を更新していって任意の歌の集合を歌った時の最小時間を求めるわけで、歌の集合が決まったとしてそれらを歌う時間が最小になる順番はtoneでソートした順だからだろう。
  • accepted in practice
class GUMIAndSongsDiv1 {
	public:
	int maxSongs(vector <int> D, vector <int> tone, int T) {
		int N=D.size();
		VVI dp(N, VI(N+1, 1<<30));
		vector<PII> w;
		REP(i, N) w.PB(MP(tone[i], D[i]));
		sort(ALL(w));
		REP(i, N) D[i]=w[i].second, tone[i]=w[i].first;
		REP(i, N) {
			dp[i][0] = 0;
			dp[i][1] = D[i];
			RANGE(n, 2, N+1) {
				REP(j, i) {
					dp[i][n] = min(dp[i][n], dp[j][n-1] + D[i] + abs(tone[i]-tone[j]));
				}
			}
		}
		int ans = 0;
		REP(i, N) REP(n, N+1) if(dp[i][n]<=T) ans = max(ans, n);
		return ans;
	}
};

2013-08-03

SRM 587 Div1 550 TriangleXor

08:46 |  SRM 587 Div1 550 TriangleXor - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 587 Div1 550 TriangleXor - kojingharangの日記  SRM 587 Div1 550 TriangleXor - kojingharangの日記 のブックマークコメント

  • 整数W, i in [0, W] に対して、(0, 1), (W, 1), (i, 0) で表される三角形の領域たちのXORの面積を求める。
  • 1≦W≦70,000
  • 図にしばし圧倒される
  • ベン図みたいに重なってるので (0, 1), (W, 1), (x, y) の三角形を足したり引いたりすればよさげ
  • (yが)一番下の三角形W+1個は +1倍
  • その上の三角形W個は -2倍
  • その上の三角形W-1個は +2倍
  • ...
  • 一番上の三角形1個は Wが奇数なら -2 倍、偶数なら 2 倍
  • したものを足してくと XOR になる
  • 各交点の高さは y=x/W と y=-x/i+1 の交点なので y=i/(i+W)
  • 最後のサンプルが合わない

(あとで)

  • 高さは上(y=1)からの距離なので 1-i/(i+W) なのであった。あと底辺 W をかけてなかった
  • 最後のサンプル以外は偶然一致していたらしい
  • accepted in practice
class TriangleXor {
	public:
	int theArea(int W) {
		long double ans = 0;
		REP(i, W+1) {
			long double h = i==0 ? 1 : 1-(long double)i/(i+W);
			long double K = i==0 ? 1 : 2*((i&1)?-1:1);
			ans += K * 0.5 * (W+1-i) * W * h;
		}
		return (int)ans;
	}
};
|