2012-02-20
SRM533 - 深夜開催のSRMがちょっと辛い今日この頃
(http://naoyat.hatenablog.jp/entry/2012/02/20/014918 からの転載)
最近ちょっと早寝早起きサイクルになってるので2amからとかちょっときついですね。
というわけでSRM533の問題を見てみた話。配点的には250-500-1000ですが・・・
出てたら430位ぐらいでレーティング横ばい〜微減、かな。
Div1 Easy(250) CasketOfStar
250がちょっと難しかった、というか正しい筋道で考えるまでに時間がかかった。
- 両端から1つずつ減らして f(a,b) = weight[a]*weight[b] + max(f(a,b-1), f(a+1,b)) みたいに解けるからDPだ、と考えていた時期が僕にもありました、みたいな
- これではサンプルケースさえ解けない。貪欲に大きい方から行くとかも何か違いそうというか怖い
- サンプル#2の {2,2,7,6,90,5,9} の例で、90 のところで左右に分けて考えられそうな気がしたので、単にあらゆる分割点を考えて f(a,b) = weight[a]*weight[b] + max( f(a,a+1)+f(a+1,b), f(a,a+2)+f(a+2,b), ..., f(a,b-1)+f(b-1,b) ) ではどうか。f(a,b) はb-a=1のとき0とすればよさげ。
- というわけでやっぱりDP。メモ化再帰で書いたけど。
- これでAC。110.83点(46'44'')
#define found(s,e) ((s).find(e)!=(s).end())
typedef pair<int,int> i_i;
map<i_i,int> mm;
int sub(const vector<int>& ws, int b, int e){
i_i k(b,e); if (found(mm,k)) return mm[k];
if (e-b <= 1) return 0;
int s = ws[b] * ws[e];
if (e-b == 2) return s;
int mx = 0;
for (int i=b+1; i<=e-1; i++) {
mx = max(mx, sub(ws,b,i) + sub(ws,i,e));
}
return mm[k] = s + mx;
}
class CasketOfStar {
public:
int maxEnergy(vector<int> weight) {
mm.clear();
return sub(weight, 0, weight.size()-1);
}
};
Div1 Medium(500) MagicBoard
51'12''かけて解いた答えが単純なテスト {"#", "#"} で落ちた。原因は S が水平方向の移動から始まらないといけないのを忘れていたから。SRMでドジっ子アピールしても何の得もないというのに。
- 一筆書きできるかどうか?
- いや、縦横縦横みたいな感じの移動でないと駄目みたい
- サンプルの最後の58(わぁい58 あかり58大好き)は間にスペースがある。移動は同一行ないし同一列ならどう飛んでもよいようだ。
- 頂点が最大2500。このどこか1つから、辺を全部(最大5000*2500ぐらい)列挙して縦横縦横縛りで移動しながらTLEせずに全部回れるのか?いや無理だろう
- 横軸から縦軸へ。縦軸から横軸へ。移動できるのはMagic Diamondのある箇所のみ。
- キーボードの配線みたいだ。そして2部グラフっぽい。
- 縦軸、横軸をノードとして、Magic Diamondを辺とすると、すべてのMagic Diamondをさらうのとすべての辺を通るのが同じなので、これはシンプルな一筆書き問題に帰着できる
- UnionFindか何かで全ノードの接続を確認した上で、各ノードから出てる辺の数を数えて、奇数の箇所が0か2なら一筆書きが可能、っていうあれだ(thanks to オイラー先生)
- コード書けた。サンプル通った。
- 提出。211.82点(51'12'')Easyで時間食ってたからこれでは間に合わない。
- しかもWA。単純なテスト {"#", "#"} で落ちてる。
- ああああああ
- S は水平方向の移動から始まらないといけないのを忘れてた
- 奇数辺のノードが2つある場合、それが縦軸ノードに2つあったらアウトだ
- そこだけ直してAC
- 問題の制約はちゃんと把握しましょう。サンプルケースが足りなければ書き足しましょう。
#include <algorithm>
#include <string>
#include <vector>
#include <set>
using namespace std;
#define rep(var,n) for(int var=0;var<(n);var++)
typedef pair<int,int> i_i;
class UnionFind {
vector<int> data;
public:
explicit UnionFind(int size) : data(size, -1) { }
bool unionSet(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 findSet(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)]; }
bool has(int x) { return data[x] >= 0; }
};
class MagicBoard {
public:
string ableToUnlock(vector<string> board) {
int R = board.size(), C = board[0].size();
// vector<set<i_i> > h(R), v(C);
vector<set<int> > hv(R), vh(C);
UnionFind uf(R+C);
rep(r,R) rep(c,C) if (board[r][c] == '#') {
i_i rc(r,c);
// h[r].insert(rc); v[c].insert(rc);
hv[r].insert(c); vh[c].insert(r);
uf.unionSet(r, R+c);
}
int root = 10000;
// int odd = 0, even = 0;
int r_odd = 0, c_odd = 0;
rep(r,R) {
if (hv[r].size() == 0) continue;
if (root == 10000) root = uf.root(r);
else if (uf.root(r) != root) return "NO";
// if (hv[r].size() & 1) odd++; else even++;
if (hv[r].size() & 1) r_odd++;
}
rep(c,C) {
if (vh[c].size() == 0) continue;
else if (uf.root(R+c) != root) return "NO";
// if (vh[c].size() & 1) odd++; else even++;
if (vh[c].size() & 1) c_odd++;
}
// return odd <= 2 ? "YES" : "NO";
if (r_odd + c_odd > 2) return "NO";
if (r_odd == 2) return "NO";
return "YES";
}
};
コメント
トラックバック - https://topcoder-g-hatena-ne-jp.jag-icpc.org/n4_t/20120220