Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2013-03-15

SRM 573 Div1 450 SkiResorts

22:56 |  SRM 573 Div1 450 SkiResorts - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 573 Div1 450 SkiResorts - kojingharangの日記  SRM 573 Div1 450 SkiResorts - kojingharangの日記 のブックマークコメント

  • N 個の地点があってその高さが A[i] で与えられる。また、地点間に道があるかどうかが road[N][N] で与えられる。
  • 道があって高さが同じか低い位置にしか移動できない。必要なら各地点の高さを変えて地点 0 から N-1 まで移動したい。
  • 高さの変化の合計の最小値を求める問題。できなければ -1 を返す。
  • 1≦N≦50
  • パスが1個決まったとして、そのパス上の高さの中間値のとこに合わせればいいのか??
  • 後まで見ないと最初の方の高さが決まらないよなぁ
  • 地点の他にその時の高さをどうにか管理する必要がありそう
  • 高さは N 通りしかないので、「ある地点をどの高さにしたか」をグラフのノードとすると最短経路っぽくなりそう
  • 書いたけど最後のやつが合わない
  • (あとで)
  • エッジのコストがなんか怪しかった。あと高さが同じか低い位置に向かうときだけエッジを張るようにした。
  • 最後に地点 N-1 での各高さにおけるポテンシャルが一番低いやつが答え。
  • priority_queue は小さいものから返してくれるようにしてほしいなぁ
  • ↓あとで(accepted in practice room)
class SkiResorts {
	public:
	long long minCost(vector <string> R, vector <int> A) {
		int N=R.size();
		ll INF = 1LL<<60;
		VI po(N*N, INF);
		priority_queue<PII, vector<PII>, greater<PII> > q;
		REP(j, N) {
			po[j]=abs(A[j]-A[0]);
			q.push(MP(po[j], j));
		}
		int end=0;
		ll ans = INF;
		while(q.size()) {
			ll pot =  q.top().first;
			int cur = q.top().second;
			int cur_n = cur/N;
			int cur_h = cur%N;
			q.pop();
			if(pot > po[cur]) continue;
			end += cur_n==N-1;
			if(end==N) break;
			REP(i, N) {
				if(R[cur_n][i]=='Y') {
					REP(j, N) {
						if(A[cur_h] < A[j]) continue;
						int cost = abs(A[j]-A[i]);
						if(po[cur] + cost < po[i*N+j]) {
							po[i*N+j] = po[cur] + cost;
							q.push(MP(po[i*N+j], i*N+j));
						}
					}
				}
			}
		}
		
		REP(i, N) ans=min(ans, po[(N-1)*N+i]);
		return ans==INF ? -1 : ans;
	}
};

2013-03-08

SRM 572 Div1 500 EllysBulls

08:45 |  SRM 572 Div1 500 EllysBulls - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 572 Div1 500 EllysBulls - kojingharangの日記  SRM 572 Div1 500 EllysBulls - kojingharangの日記 のブックマークコメント

  • ある K 桁の数字を N 回当てるゲームをする。推測した K 桁の数字 G[i] と、それに対して当たった桁の個数 B[i] が与えられる。
  • 正解の数字が存在しないなら "Liar", 1 通りに定まるならそれを, 2 通り以上あるなら "Ambiguity" を返すという問題。
  • 1≦N≦50, 2≦K≦9
  • 10**9 通りは試せない
  • だめそうだよなぁと思いつつも、有りうる候補を管理して明らかにだめなやつを消していく作戦
  • いろいろ書いてうまくいかないうちに終了。
  • あとで
  • 半分全列挙らしい
  • 前半 K/2 桁に対して各推測が何個一致するかを求める。残り何個一致しなきゃいけないかわかるのでそれを map に記録
  • 後半 K-K/2 桁に対して同様のことをやって map を引いて、あったら答えを構築
  • 後半に対して前半が 2 個以上あったり、すでに構築されてたりするときは Ambiguity
  • なんかいろいろ出力しすぎ
  • ↓あとで(accepted in practice room)
class EllysBulls {
	public:
	string getNumber(vector <string> G, vector <int> B) {
		int N=G.size(), K=G[0].size();
		map<VI, PII> rests;
		int K0=K/2, K1=K-K0;
		VI ten(1, 1);
		REP(i, 6) ten.PB(ten.back()*10);
		cout<<ten<<endl;
		cout<<K0<<" "<<K1<<endl;
		//return "";
		REP(v, ten[K0]) {
			//cout<<"p1 "<<v<<endl;
			char buf[20];
			sprintf(buf, "%0*d", K0, v);
			//cout<<"buf: "<<string(buf)<<endl;
			VI rest(N);
			int ok=1;
			REP(gi, N) {
				int match=0;
				REP(k, K0) match += buf[k]==G[gi][k];
				if(B[gi]-match<0) {ok=0;break;}
				rest[gi] = B[gi]-match;
			}
			if(ok) {
				rests[rest].first++;
				rests[rest].second = v;
			}
		}
		//cout<<rests<<endl;
		string ans;
		REP(v, ten[K1]) {
			char buf[20];
			sprintf(buf, "%0*d", K1, v);
			VI key(N);
			REP(gi, N) {
				int match=0;
				REP(k, K1) match += buf[k]==G[gi][K0+k];
				key[gi] = match;
			}
			//cout<<"key: "<<key<<endl;
			if(rests.count(key)) {
				//cout<<"p2 count "<<v<<" "<<rests.count(key)<<endl;
				if(rests[key].first > 1) return "Ambiguity";
				if(ans.size()) return "Ambiguity";
				char buf[30];
				sprintf(buf, "%0*d", K0, rests[key].second);
				sprintf(&buf[K0], "%0*d", K1, v);
				ans = string(buf);
			}
		}
		if(ans.size()==0) return "Liar";
		return ans;
	}
};

2013-03-02

TCO Round1B 500 EllysFigurines

05:12 |  TCO Round1B 500 EllysFigurines - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO Round1B 500 EllysFigurines - kojingharangの日記  TCO Round1B 500 EllysFigurines - kojingharangの日記 のブックマークコメント

  • 15x15 以内の白黒ビットマップが与えられる。1回に 1~R 行または 1~C 列分、白にできる。最小何回で全部白にできるか。
  • 全然思いつかない
  • dp[x][y][w][h] で [x,x+w) x [y,y+h) を白にする最小回数?とか思って書いてみる
  • バグってるうちに終了
  • そもそもこれはだめだ(領域を分断すると本来1回で消せても2回以上に分割されちゃうことがある)
  • あとで
  • 消す行たち(最大2**15 通り)を決めると黒が残ってる列が決まって、それらを左から順に消してけば回数が出る
  • 気が付かなかった。。
  • ↓あとで(accepted in practice room)
class EllysFigurines {
	public:
	int getMoves(vector<string> B, int R, int C) {
		int W=B[0].size();
		int H=B.size();
		int ans = 1<<30;
		REP(bit, 1<<H) {
			int lans=0;
			int rmy[20]={};
			int rest[20]={};
			REP(y, H) {
				if((bit>>y)&1) {
					lans++;
					REP(i, R) if(y+i<H) rmy[y+i]=1;
				}
				if(!rmy[y]) REP(x, W) if(B[y][x]=='X') rest[x]=1;
			}
			REP(x, W) {
				if(rest[x]) {
					lans++;
					x+=C-1;
				}
			}
			ans=min(ans, lans);
		}
		return ans;
	}
};

TCO Round1B 1000 EllysReversals

05:12 |  TCO Round1B 1000 EllysReversals - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO Round1B 1000 EllysReversals - kojingharangの日記  TCO Round1B 1000 EllysReversals - kojingharangの日記 のブックマークコメント

  • あとで
  • 最大50文字の文字列が最大50個ある。どれかの文字列の先頭の偶数文字を逆順にする作業を 0 回以上行う。
  • 同じ文字列が2つ出てきたらそれらは消される。最後に残る文字列の最小個数を求める問題。
  • 先頭から2文字ずつのグループに分けると、グループ内の順序とグループの順序は任意に動かせる
  • (任意のグループを先頭に持ってきたり元の位置に戻したりできるので)
  • ので、ある文字列について何回か作業したときにできる文字列の代表を求める(辞書順最小のものとか)
  • 各文字列の代表の個数が奇数個のものだけ 1 個ずつ残るのでその和が答え。

  • ↓あとで(accepted in practice room)
class EllysReversals {
	public:
	int getMin(vector <string> W) {
		int N=W.size();
		map<string, int> hi;
		REP(i, N) {
			vector<string> ww;
			REP(j, W[i].size()/2*2) {
				string ss;
				ss.PB(W[i][j]);
				ss.PB(W[i][j+1]);
				j++;
				sort(ALL(ss));
				ww.PB(ss);
			}
			sort(ALL(ww));
			string sn;
			FOR(e, ww) sn+=*e;
			if(W[i].size() & 1) sn+=W[i][W[i].size()-1];
			hi[sn]++;
		}
		cout<<hi<<endl;
		int ans = 0;
		FOR(e, hi) if(e->second & 1) ans++;
		return ans;
	}
};

2013-02-24

TCO Round1A 500 TheFrog

10:54 |  TCO Round1A 500 TheFrog - kojingharangの日記 を含むブックマーク はてなブックマーク -  TCO Round1A 500 TheFrog - kojingharangの日記  TCO Round1A 500 TheFrog - kojingharangの日記 のブックマークコメント

  • 数直線上で常に +x だけジャンプして進めるカエルが 0 にいて、(L[i], R[i]) の穴に落ちずに D 以上のとこまで進みたい。x の最小値を求める。
  • 1≦D≦30,000, 1≦L,Rの要素数≦50, 0≦L[i]≦D-1, L[i]+1≦R[i]≦D, 穴は重複しない
  • 二分探索→だめ(サンプルにあってよかった)
  • 最小の x ということは、必ず R[i] のどれかを通るはず
  • 各 R[i] について、 R[i] まで R[i]歩で来る場合、R[i]-1歩で来る場合、... 2歩で来る場合、1 歩で来る場合それぞれについて穴に落ちずに行けるかを調査する
  • 調査するとこを naive に書いて提出
  • 50*30000*30000*50 になるからだめだ
  • 穴に落ちないかどうかは穴の手前の地点を見つけてそこから一歩行った所が穴じゃなければいいというふうに書き換え。50*30000*50 になったので提出
  • wrong answer
  • double の誤差死
  • ↓あとで整数比較に直した版(accepted in practice room)
class TheFrog {
	public:
	double getMinimum(int D, vector <int> L, vector <int> R) {
		double ans = D*2;
		REP(i, L.size()) { //50
			for(ll div=R[i];div>=1;div--) {//30000
				double mid=(double)R[i]/div; // mid は二分探索のなごり
				int ok=1;
				REP(j, L.size()) {
					// EPS つけたらこっちもOKだった
					//double p0 = floor(L[j]/mid+1e-9)*mid;
					//if(p0 + mid < R[j]-1e-9) {ok=0;break;}
					
					ll lp1 = (L[j]*div/R[i]+1) * R[i];
					if(lp1<R[j]*div) {ok=0;break;}
				}
				if(ok) {
					ans=min(ans, mid);
					break;
				}
			}
		}
		return ans;
	}
};

2013-02-19

SRM 570 Div1 250 RobotHerb

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

  • 長さ N の数列 A に対して、「i=0~N-1 に対して順番に、前進を A[i] 回→右90度回転を A[i] 回する操作」を T 回行う。最初の位置から最後の位置までのマンハッタン距離を求める問題。
  • 1≦T≦1,000,000,000, 1≦N≦50, 1≦A[i]≦10,000,000
  • どんな A でも、4回繰り返すと最初の方向と同じ向きになるので
  • 4回繰り返した時の移動量を T/4 倍すると T/4*4 回繰り返したのと同じになる
  • 残りの T%4 回はシミュレート
  • 方針が決まっても、簡潔に書くには関数に分けるとしたらどこを切り出すべきか、どんな引数にするのか、後で使うから参照にするのかどうなのかとかでちょっと悩む
  • 結果的に go の引数に dir は不要だった。
  • Accepted
#define ABS(x) (x>=0 ? x : (-x))

class RobotHerb {
	public:
	
	void go(vector<int>& A, ll& x, ll& y, ll& dir) {
		ll dx[] = {0,1,0,-1};
		ll dy[] = {1,0,-1,0};
		REP(i, A.size()) {
			x+=dx[dir]*A[i];
			y+=dy[dir]*A[i];
			dir = (dir+A[i]%4) % 4;
		}
	}
	long long getdist(int T, vector <int> A) {
		ll x4=0,y4=0,dir=0;
		REP(i, 4) go(A, x4, y4, dir);
		ll x=x4*(T/4),y=y4*(T/4);
		cout<<x4<<" "<<y4<<endl;
		REP(i, T%4) go(A, x, y, dir);
		cout<<x<<" "<<y<<endl;
		//return llabs(x)+llabs(y);
		return ABS(x)+ABS(y);
	}
};
|