TopCoder SRM 617 Div1 Medium PieOrDolphin
問題
50人の人がいて、n日間二人ずつプレゼントが渡される。
i日目にプレゼントを渡される人はchoice1[i]およびchoice2[i]である。
プレゼントはPieまたはDolphinで、どちらが渡されるかは決まってない。
全てのプレゼントを渡し終えた後、
各人が持っている|Pieの個数 - Dolphinの個数|の和が最小になるようにプレゼントを配りたい。
プレゼントの配り方をどれか一通り出力せよ。
制約条件
n≦1000
choice1[i], choice2[i]≦50
choice1[i]≠choice2[i]
方針
それぞれの人を頂点として、(choice1[i], choice2[i])に辺があるようなグラフを考える。
Pieを得ることを+1, Dolphinを得ることを-1として、それぞれの人の得点の絶対値の和を最小化する問題としてよい。
渡すプレゼントを選ぶのは、辺のどちら側の頂点に1を足して、どちら側の頂点から1を引くかという操作に置き換えられるので、辺の向き付けを考えることになる。
向き付けをおこなった後、ループができると、そこでの失点がゼロになる。
なのでなるべくループを作りたい。
ループはどのような順番で作っても出来る数は一緒(次数を2ずつ消費する)なので、
貪欲というか取れる順に適当に取っていってしまってかまわない。
森が残るが、パス1本につき失点が2増えるので、
なるべく少ないパスで森の辺を被覆したい。
これは次数が1の頂点から適当にパスを取っていけば達成される。
ソースコード
class PieOrDolphin { public: int n; vi ans, c1, c2; map<pi, int> id; vector <int> Distribute(vector <int> choice1, vector <int> choice2) { c1 = choice1; c2 = choice2; n = c1.size(); id.clear(); ans.clear(); ans.resize(n); rep(i, n){ int a = choice1[i], b = choice2[i]; if(a > b) swap(a, b); if(id.count(mp(a, b))){ int prev = id[mp(a, b)]; fix(prev, a, b); fix(i, b, a); id.erase(mp(a, b)); } else id[mp(a, b)] = i; } //remove loop vector<vi> e(50); { int st[100], sz; bool v[100]; each(i, id){ int a = i->first.first, b = i->first.second; e[a].pb(b); e[b].pb(a); } while(1){ memset(v, 0, sizeof(v)); bool fin = 1; rep(i, 50) if(!v[i]){ sz = 0; if(!check_loop(i, i, e, st, sz, v)) continue; rep(j, sz){ int a = st[j], b = st[(j + 1) % sz]; int eid = id[mp(min(a, b), max(a, b))]; fix(eid, a, b); erase(e, a, b); erase(e, b, a); } fin = 0; break; } if(fin) break; } } #if 0 rep(i, 50) if(e[i].size()){ cerr<<"i: "<<i<<" sz: "<<e[i].size()<<endl; } #endif //pick a tree and solve it while(1){ bool fin = 1; rep(i, 50) if(e[i].size() == 1){ int st[100]; int sz = 0; int c = i, p = i; while(1){ st[sz++] = c; int next = -1; rep(j, e[c].size()) if(e[c][j] != p){ next = e[c][j]; break; } if(next < 0) break; p = c; c = next; } //rep(j, sz) cerr<<st[j]<<" ";cerr<<endl; rep(j, sz - 1){ int a = st[j], b = st[j + 1]; int eid = id[mp(min(a, b), max(a, b))]; fix(eid, a, b); erase(e, a, b); erase(e, b, a); } fin = 0; break; } if(fin) break; } return ans; } bool check_loop(int c, int p, const vector<vi> &e, int *st, int &sz, bool *v){ st[sz++] = c; v[c] = 1; rep(i, e[c].size()) if(e[c][i] != p){ if(v[e[c][i]]){ int pos; for(pos = 0; pos < sz && st[pos] != e[c][i]; pos++); for(int j = pos; j < sz; j++) st[j - pos] = st[j]; sz -= pos; return 1; } if(check_loop(e[c][i], c, e, st, sz, v)) return 1; } sz--; return 0; } void erase(vector<vi> &e, int a, int b){ for(int i = (int)e[a].size() - 1; i >= 0; i--) if(e[a][i] == b) e[a].erase(e[a].begin() + i); } void fix(int idx, int a, int b){ if(c1[idx] == a) ans[idx] = 1; else ans[idx] = 2; } };