Hatena::Grouptopcoder

kojingharangの日記 RSSフィード

|

2012-01-15

SRM 529 Div2

11:24 | SRM 529 Div2 - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 529 Div2 - kojingharangの日記 SRM 529 Div2 - kojingharangの日記 のブックマークコメント

237.78 WA opend 1152->1086

引き続き暴落中... そろそろ上がりたく。

SMR 529 Div2 medium KingSort

11:24 |  SMR 529 Div2 medium KingSort - kojingharangの日記 を含むブックマーク はてなブックマーク -  SMR 529 Div2 medium KingSort - kojingharangの日記  SMR 529 Div2 medium KingSort - kojingharangの日記 のブックマークコメント

王の名前とN世が文字列で与えられるので、王の名前とローマ数字の昇順にソートして返す問題。

ローマ数字→intのルーチンで一桁目が 0 の場合に何も返してなくて failed system test. ぬるい。

手元のコンパイルのとき -Wall くらい付けたほうがいいな。

(あとで通したもの)

class KingSort {
	public:
	int ord(string s) {
		//The Roman numerals for 1 through 10 are I, II, III, IV, V, VI, VII, VIII, IX, and X.
		//The Roman numerals for 20, 30, 40, and 50 are XX, XXX, XL, and L.
		if(s.substr(0,1)=="L") return 50+ord(s.substr(1));
		if(s.substr(0,2)=="XL") return 40+ord(s.substr(2));
		if(s.substr(0,3)=="XXX") return 30+ord(s.substr(3));
		if(s.substr(0,2)=="XX") return 20+ord(s.substr(2));
		if(s.substr(0,1)=="X") return 10+ord(s.substr(1));
		if(s.substr(0,2)=="IX") return 9;
		if(s.substr(0,4)=="VIII") return 8;
		if(s.substr(0,3)=="VII") return 7;
		if(s.substr(0,2)=="VI") return 6;
		if(s.substr(0,1)=="V") return 5;
		if(s.substr(0,2)=="IV") return 4;
		if(s.substr(0,3)=="III") return 3;
		if(s.substr(0,2)=="II") return 2;
		if(s.substr(0,1)=="I") return 1;
		return 0;  // <- これ!
	}
	vector <string> getSortedList(vector <string> kings) {
		vector<pair<pair<string, int>, string> > w;
		REP(i, kings.size()) {
			stringstream ss(kings[i]);
			string name, o;
			ss>>name>>o;
			w.push_back(make_pair(make_pair(name, ord(o)), kings[i]));
		}
		sort(ALL(w));
		vector<string> ans(w.size());
		REP(i, w.size()) ans[i]=w[i].second;
		return ans;
	}

SMR 529 Div2 hard MinskyMysteryDiv2

11:24 |  SMR 529 Div2 hard MinskyMysteryDiv2 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SMR 529 Div2 hard MinskyMysteryDiv2 - kojingharangの日記  SMR 529 Div2 hard MinskyMysteryDiv2 - kojingharangの日記 のブックマークコメント

与えられた手順があるので、停止するかどうかと停止したときの変数の値を求める問題。

40分くらい紙の上で考えたけど最適化できず。あとから考えるとコードを動かしながら考えれば良かった。。

(あとで)

bag0 bag1の大小で分けると、bag0 < bag1なら停止しない、そうでなければ bag0%bag1==0なら停止して bag1+bag0/bag1 が答え。

割り切れない場合は、bag0=bag2 のとこで実際にいくつか出力してみると bag0 は元々の bag0 と同じになりそうということがわかったので

bag2 関連は無視してよくて(ここがポイントっぽい)、bag1 は 1 ずつ増えて行って bag0==bag1 になったら bag0%bag1==0 になるので停止するので bag0%bag1 != 0 の場合もいずれ停止する。

というわけで N<2 なら -1, そうでなければ N の最初の約数 i に対して i+N/i を返せばよい。

O(sqrt(N)) なので 10^12 でもOK.

(あとで通したもの)

class MinskyMysteryDiv2 {
	public:
	long long computeAnswer(long long N) {
		if(N<2) return -1;
		if(N%2==0) return 2+N/2;
		for(ll i=3;i*i<=N;i+=2) {
			if(N%i==0) return i+N/i;
		}
		return N+1;
	}

2012-01-12

SRM 417 medium CubeNets (practice)

23:21 | SRM 417 medium CubeNets (practice) - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 417 medium CubeNets (practice) - kojingharangの日記 SRM 417 medium CubeNets (practice) - kojingharangの日記 のブックマークコメント

展開図が立方体になるかどうかを判定する問題。

dfs しながら立方体の底に1を貼りつけていく。回転させて再帰呼び出しして回転を戻す。

(当初戻すコードを入れてなくて入れてみたら通ったけどほんとは最初にこれで行くって思いついて検証してから書き始めなければいかんなぁ....)

int f(int x, int y, vector<string>& fig, VI& w) {
	//cout<<x<<" "<<y<<" "<<fig[y][x]<<endl;
	fig[y][x]='-';
	int dx[4]={-1, 1, 0, 0};
	int dy[4]={0, 0, -1, 1};
	int rot[4][6]={
		{2,3,1,0,4,5}, //left
		{3,2,0,1,4,5}, //right
		{5,4,2,3,0,1}, //up
		{4,5,2,3,1,0}, //down
	};
	if(w[0]) return 0;
	w[0]=1;
	if(accumulate(ALL(w), 0)==6) return 1;
	REP(i, 4) {
		int yy=y+dy[i];
		int xx=x+dx[i];
		VI nw(6);
		REP(j, 6) nw[rot[i][j]]=w[j];
		if(0<=yy && yy<fig.size() && 0<=xx && xx<fig[0].size() && fig[yy][xx]=='#') if(f(xx, yy, fig, nw)) return 1;
		REP(j, 6) w[j]=nw[rot[i][j]];  // ←最初入れてなかった
	}
	return 0;
}

class CubeNets {
	public:
	string isCubeNet(vector <string> fig) {
		VI w(6);
		REP(y, fig.size()) REP(x, fig[0].size()) if(fig[y][x]=='#') return f(x, y, fig, w) ? "YES":"NO";
	}

2012-01-09

SRM 練習

19:20 | SRM 練習 - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 練習 - kojingharangの日記 SRM 練習 - kojingharangの日記 のブックマークコメント

最近は Div1 easy, medium 中心にこつこつ練習。

SRM 420 Div1 medium RedIsGood (practice)

19:20 |  SRM 420 Div1 medium RedIsGood (practice) - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 420 Div1 medium RedIsGood (practice) - kojingharangの日記  SRM 420 Div1 medium RedIsGood (practice) - kojingharangの日記 のブックマークコメント

最適に選んだときの期待値は、残っている Red, Black の数が決まると定まる。

( f(5, 1) の手計算でなかなか数が合わず苦労..)

漸化式は

f(r, b) = 0 (r=0)
        = r (b=0)
        = max(0, ( r * (1 + f(r-1, b)) + b (-1 + f(r, b-1)) ) / (r+b)) (else)

で、いつもならメモ化再帰を使うところだが、double [5001][5001] は 64MB に収まらないと気づいて、

ループDP+メモリ使い回しを用いて double [5001] x 2 で済むようにした。

あと前 CatchTheMice で double だと微妙な誤差で通らなくて long double だと通ったトラウマ?があるので、誤差が 1e-9 までとかの場合は念のため long double を使うようになったでござる。

class RedIsGood {
	public:
	double getProfit(int R, int B) {
		vector<long double> dp(R+1);
		vector<long double> ndp(R+1);
		REP(r, R+1) dp[r]=r;
		for(int b=1;b<=B;b++) {
			ndp[0]=0.0;
			for(int r=1;r<=R;r++) {
				long double v = (r*(1+ndp[r-1]) + b*(-1+dp[r]))/(r+b);
				ndp[r] = max(v, 0.0l);
			}
			//cout<<ndp<<endl;
			dp=ndp;
		}
		return dp[R];
	}

2011-12-29

SRM 練習

14:49 |  SRM 練習 - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 練習 - kojingharangの日記  SRM 練習 - kojingharangの日記 のブックマークコメント

いくつか練習。

SRM 433 Div1 easy MagicWords

14:49 |  SRM 433 Div1 easy MagicWords - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 433 Div1 easy MagicWords - kojingharangの日記  SRM 433 Div1 easy MagicWords - kojingharangの日記 のブックマークコメント

文字列を回転させたもののうち、元の文字列と一致するものが K 個になるようなのを magic word ということにして、

いくつかの文字列をいろんな順序でつなげたもののうち magic word になるようなやつの個数を求める。

  • 文字列の個数は最大8個なので、8! = 40320 ということでこれはそのまま全部試せばよさそう。
  • くっつけた文字列の長さ L の最大値は 8*20=160。magic word 判定は普通にやると長さ^2なので、160 * 160 * 40 320 = 1,032,192,000となってちょっと微妙そうな感じがする
  • 回転して元と一致する個数が k 個なら、その文字列は長さL/kの文字列が k 個つながってる という性質を利用
  • 先頭の L/k 文字と L/k*j からの L/k 文字がすべて一致する → 一致するものは k 個以上
  • このチェックだと一致する個数が 2 つの場合は 4 つの場合に含まれるので、一致する個数を大きい方から L~1 として見ていく。
  • k 個一致したらそこで終わりなので K と同じなら答えを1コ増やす。
  • N^2 っぽいけど、L mod k != 0 のときはチェックを端折れるので N^1.5 ほどになると思う。
class MagicWords {
	public:
	int count(vector <string> S, int K) {
		int N=S.size();
		VI p(N);
		REP(i, N) p[i]=i;
		int L = 0;
		REP(i, N) L+=S[i].size();
		if(L%K==0) {
			int ans = 0;
			do {
				string s = "";
				REP(i, N) s+=S[p[i]];
				for(int l=1;l<=L;l++) {
					if(L%l!=0) continue;
					int k=L/l;
					int ok=1;
					REP(i, L/k) REP(j, k) if(s[i]!=s[j*L/k+i]) ok=0;
					if(ok) {
						if(k==K) ans++;
						break;
					}
				}
				//cout<<s<<"  "<<ok<<endl;
			} while(next_permutation(ALL(p)));
			return ans;
		}
		return 0;
	}

SRM 363 Div1 easy HandsShaking

14:49 |  SRM 363 Div1 easy HandsShaking - kojingharangの日記 を含むブックマーク はてなブックマーク -  SRM 363 Div1 easy HandsShaking - kojingharangの日記  SRM 363 Div1 easy HandsShaking - kojingharangの日記 のブックマークコメント

社畜ビジネスマンが N 人円卓に座って、腕が交差しないように全員が握手する方法は何通りあるか。

  • f(n-k) から f(n) が作れないかいろいろ試行錯誤
  • ....
  • ....
  • 一組握手してしまえば、その左右は独立した領域になるので、f(n)=Σf(左の人数)*f(右の人数) で良さそう
  • それをメモ化。


ll memo[60];
ll f(int n) {
	ll ans = 0;
	if(n==0) return 1;
	if(memo[n]) return memo[n];
	for(int i=0;i<=n-2;i+=2) {
		int j=n-2-i;
		ans+=f(i)*f(j);
	}
	return memo[n]=ans;
}

class HandsShaking {
	public:
	long long countPerfect(int n) {
		memset(memo, 0, sizeof(memo));
		return f(n);
	}

成功体験++

SRM 528 Div2

14:07 | SRM 528 Div2 - kojingharangの日記 を含むブックマーク はてなブックマーク - SRM 528 Div2 - kojingharangの日記 SRM 528 Div2 - kojingharangの日記 のブックマークコメント

緑の人として参加。

225.99 WA WA 1191 -> 1152

ひきつづき緑の人。

250 MinCostPalindrome

14:07 |  250 MinCostPalindrome - kojingharangの日記 を含むブックマーク はてなブックマーク -  250 MinCostPalindrome - kojingharangの日記  250 MinCostPalindrome - kojingharangの日記 のブックマークコメント

  • 回文になるように ? を x か o に置き換えるときの最小コストを求める
  • 奇数の長さに対応してないコードに対してチャレンジしようとしたら、問題条件に書いてある通り長さは偶数だぜと言われる orz
class MinCostPalindrome {
	public:
	int getMinimum(string s, int oCost, int xCost) {
		int ans = 0;
		REP(i, s.size()/2) {
			if(s[i]=='?'&&s[s.size()-i-1]=='?') ans += min(oCost, xCost)*2;
			else if(s[i]=='?') ans += s[s.size()-i-1]=='o'?oCost:xCost;
			else if(s[s.size()-i-1]=='?') ans += s[i]=='o'?oCost:xCost;
			else if(s[i]!=s[s.size()-i-1]) return -1;
		}
		if(s.size()&1) if(s[s.size()/2]=='?') ans += min(oCost, xCost);
		return ans;
	}

500 Cut

14:07 |  500 Cut - kojingharangの日記 を含むブックマーク はてなブックマーク -  500 Cut - kojingharangの日記  500 Cut - kojingharangの日記 のブックマークコメント

  • eel ってうなぎらしい。
  • {14 20} 1 みたいなケースを考えてなくて failed system test.
  • あからさまにソートしてなかったのにチャレンジされなかった部屋ということで。

↓あとで通したもの

class Cut {
	public:
	int getMaximum(vector <int> es, int mc) {
		int ans = 0;
		sort(ALL(es));
		REP(jj, 2)
		FOR(v, es) {
			if(!(jj==0&&*v%10==0 || jj==1&&*v%10>0)) continue;
			while(*v>10 && mc>0) {
				*v-=10;
				ans++;
				if(*v>0) mc--;
			}
			if(*v==10) ans++;
		}
		return ans;
	}

1000 Mosquitoes

14:07 |  1000 Mosquitoes - kojingharangの日記 を含むブックマーク はてなブックマーク -  1000 Mosquitoes - kojingharangの日記  1000 Mosquitoes - kojingharangの日記 のブックマークコメント

数直線上で蚊がそれぞれの位置から等速直線運動をする。時刻と円の中心を適切に決めて半径R以内に入る蚊の最大数を求める。

  • 蚊の数も初期位置の範囲も小さいし、t>=200くらいでみんな外向きに広がってっちゃうので 0~300 を微小時間に分けて円の端の位置を全通り試してみよう
  • 最大の速さ 100 なので 0.01 秒単位でいいかな
  • サンプルと、速さ100のときのテストも通ったので出したら failed system test.
  • よーく追って見ると、0.777777777... のときに一瞬だけ 4 匹が半径 1 以内に入るケースで 3 匹と答えていた。。
  • 物理シミュレーションで薄い壁をボールが通り抜けちゃったみたいな感じ。
  • 任意の2匹の蚊の距離が 2R になる瞬間すべてについて位置を求めてソートしてどこまで R に入ってるか確かめると良いらしい。
  • 変化は連続なんだけど求める答えが変わらないような有限のサンプルに落とすという例であろう。

↓最初に書いただめなやつ

class Mosquitoes { 
  public: 
  int getMaximum(vector <int> xInit, vector <int> v, int R) { 
    int ans = -1; 
    //int K=2; 
    int KT=100; 
    REP(idx, v.size()) 
    for(int side=-1;side<=1;side+=2) 
    { 
      for(int t=0;t<300*KT;t++) 
      { 
        double x = (xInit[idx]+(double)t/KT*v[idx])+side*R; 
        int lans = 0; 
        REP(i, v.size()) lans += fabs(x - (xInit[i]+(double)t/KT*v[i]))<=R; 
        ans = max(ans, lans); 
      } 
    } 
    return ans; 
  } 

↓後で通したやつ

class Mosquitoes {
	public:
	int getMaximum(vector <int> xInit, vector <int> v, int R) {
		int ans = -1;
		vector<double> ts;
		int N = v.size();
		REP(i, N) REP(j, N) {
			if(i==j || v[i]==v[j]) continue;
			double t0 = (xInit[i]-xInit[j]-2.0*R)/(v[j]-v[i]);
			if(t0>=0.0) ts.push_back(t0);
			double t1 = (xInit[i]-xInit[j]+2.0*R)/(v[j]-v[i]);
			if(t1>=0.0) ts.push_back(t1);
		}
		ts.push_back(0.0);
		//cout<<ts<<endl;
		vector<double> pos(N);
		FOR(t, ts) {
			REP(i, N) pos[i] = xInit[i]+*t*v[i];
			sort(ALL(pos));
			REP(i, N) {
				for(int j=i;j<N;j++) {
					if(pos[j] - pos[i] <= 2.0*R+0.00000001) ans = max(ans, j-i+1);
				}
			}
		}
		return ans;
	}

2011-12-20

SRM 527 Div1

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

practice で。

275 P8XGraphBuilder

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

総当りで TLE、その後 editorial 見てヒントをもらって書いた(practice system test pass)

辺の数が頂点数-1で連結なので木になる。

残りが remain 個あったとして、どれかの葉に i 個の子ノードを加える操作を(できるなら)したときの最大 score を返す。

(葉だけに加えてよいのは、葉でないノードに加える選択肢は以前のどこかで葉に加える操作に含まれるので)

その際、ルートノードだったら次数 i のノードが1個増え、次数1の子ノードが i 個増える。

ルートじゃないときは、次数1のノード(親への辺の分)が1個減り、次数 i+1 のノードが1個増え、次数1の子ノードが i 個増える。

で最初にルートが置いてあるとして、remain == 頂点数-1 として再帰関数をよびだす。

それをメモ化。

全然DP脳じゃないけどメモ化再帰脳には少しなってきたような気がするw

class P8XGraphBuilder {
   public:
   VI dp;
   int f(vector<int>& scores, int remain) {
       if(remain==0) return 0;
       if(dp[remain]!=-1) return dp[remain];
       int ans = -100;
       for(int i=1;i<=remain;i++) {
           //cout<<"remain "<<remain<<" take "<<i<<" "<<scores[i]<<" "<<endl;
           int a = f(scores, remain-i);
           //cout<<"remain "<<remain<<" take "<<i<<" "<<scores[i]<<" "<<a<<" -> "<<-scores[0]+scores[i]+i*scores[0]+a<<endl;
           ans = max(ans, (remain<scores.size()?-scores[0]+scores[i]:scores[i-1])+i*scores[0]+a);
       }
       dp[remain] = ans;
       return ans;
   }
   int solve(vector <int> scores) {
       dp.assign(54, -1);
       int ans = f(scores, scores.size());
       return ans;
   }
   

↓最初に書いた総当たりのコード

ルートだけの状態から出発して、既存ノードの次数ごとにノードを追加してヒストグラムを更新。

結構同じ状態があるんじゃないかと思ったけどそうでもなく爆発。

(↓だめな例)

class P8XGraphBuilder {
   public:
   int solve(vector <int> scores) {
       set<VI > se;
       VI a;
       a.push_back(2);
       se.insert(a);
       int ans = -100;
       int co=0;
       while(se.size()) {
           co++;
           const VI& a = *se.begin();
           //cout<<"pop "<<a<<endl;
           if(a.size()==scores.size()) {
               int lans = 0;
               REP(i, a.size()) lans+=scores[i]*a[i];
               ans=max(ans, lans);
           } else {
               REP(i, a.size()) {
                   VI b(a);
                   b.push_back(0);
                   if(b[i]>0) {
                       b[i]-=1;
                       b[i+1]+=1;
                       b[0]++;
                       //cout<<b<<endl;
                       se.insert(b);
                   }
               }
           }
           se.erase(se.begin());
       }
       cout<<co<<endl;
       return ans;

450 P8XMatrixRecovery

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

解説とかいろいろ見てからコードだけ書いた。(practice system test pass)

2部マッチング問題の実装練習ということで。

maximumFlow() は spaghetti sourceさんより拝借。


すべての「?」について、まず0として列と列のマッチングに成功したら次へ、失敗したら1にして次へ。

最後に残ったものが辞書順最小かつヒントに矛盾しない答え。

こういう、いくつかのアルゴリズムを組み合わせる系ってすげぇと思う。

なんとかの最大値を求めるために解を仮定して二分探索するやつとか。

#define INF 1000000
typedef int Weight;
struct Edge {
 int src, dst;
 Weight weight;
 Edge(int src, int dst, Weight weight) :
   src(src), dst(dst), weight(weight) { }
};
bool operator < (const Edge &e, const Edge &f) {
 return e.weight != f.weight ? e.weight > f.weight : // !!INVERSE!!
   e.src != f.src ? e.src < f.src : e.dst < f.dst;
}
typedef vector<Edge> Edges;
typedef vector<Edges> Graph;

typedef vector<Weight> Array;
typedef vector<Array> Matrix;

#define RESIDUE(s,t) (capacity[s][t]-flow[s][t])
Weight maximumFlow(const Graph &g, int s, int t) {
 int n = g.size();
 Matrix flow(n, Array(n)), capacity(n, Array(n));
 REP(u,n) FOR(e,g[u]) capacity[e->src][e->dst] += e->weight;

 Weight total = 0;
 while (1) {
   queue<int> Q; Q.push(s);
   vector<int> prev(n, -1); prev[s] = s;
   while (!Q.empty() && prev[t] < 0) {
     int u = Q.front(); Q.pop();
     FOR(e,g[u]) if (prev[e->dst] < 0 && RESIDUE(u, e->dst) > 0) {
       prev[e->dst] = u;
       Q.push(e->dst);
     }
   }
   if (prev[t] < 0) return total; // prev[x] == -1 <=> t-side
   Weight inc = INF;
   for (int j = t; prev[j] != j; j = prev[j])
     inc = min(inc, RESIDUE(prev[j], j));
   for (int j = t; prev[j] != j; j = prev[j])
     flow[prev[j]][j] += inc, flow[j][prev[j]] -= inc;
   VI path;
   for (int j = t; prev[j] != j; j = prev[j]) path.push_back(j);
   path.push_back(s);
   reverse(ALL(path));
   total += inc;
   //cout<<"Update "<<path<<" -> total "<<total<<endl;
 }
}
void add_edge(Graph& g, int s, int e, int w) {
   g[s].push_back(Edge(s, e, w));
   g[e].push_back(Edge(e, s, 0));
}
class P8XMatrixRecovery {
   public:
   vector <string> solve(vector <string> rows, vector <string> col) {
       int R = rows.size();
       int C = rows[0].size();
       REP(yy, R) {
           REP(xx, C) {
               if(rows[yy][xx]!='?') continue;
               rows[yy][xx] = '0';
               int t = C*2+2-1;
               //cout<<"BEGIN "<<xx<<" "<<yy<<endl;
               Graph g(C*2+2, Edges());
               REP(x, C) add_edge(g, 0, 1+x, 1);
               REP(x, C) add_edge(g, 1+C+x, t, 1);
               REP(x, C)
               REP(x1, C) {
                   int ee = 1;
                   REP(y, R) {
                       if(rows[y][x]=='?' || col[x1][y]=='?' || rows[y][x]==col[x1][y]) {
                       } else ee=0;
                   }
                   if(ee) add_edge(g, 1+x, 1+C+x1, 1);
               }
               if(C!=maximumFlow(g, 0, t)) {
                   rows[yy][xx] = '1';
               }
               //cout<<"RESULT "<<rows[yy][xx]<<endl;
           }
       }
       return rows;
   }

|