TopCoder SRM442 Div1Hard
問題
Nowhere Landの王様が、国のいくつかの街に警備を置こうとしている。国にはK個の警備会社がある。最初から警備が置かれている街がいくつかあり、また警備を新たに置くことができる街は、警備会社が機関を置いている街だけである。また、いくつかの街の間には協約があって、「ある警備会社が、片方の街だけ警備していて、もう片方の街は警備していない」なら、1つの対立が生じる。対立の数をできるだけ少なくするとき、その最小値を返せ。
解法
警備会社ごとに独立な問題として考えられる。
各街と、起点、終点を持つ有向グラフを考える。ある街が、「機関がなくて警備員が置けない」なら、「起点→その街」を容量無限大の辺で結ぶ。「最初から警備員が置かれている」なら、「その街→終点」を容量無限大の辺で結ぶ。また、関係がある街同士は、容量1の双方向の辺で結ぶ。起点→終点の最大流を求めると、その警備会社による「対立の数」の最小値が求まる。
いったい何をやっているかというと、「警備員を置かない街」と「警備員を置く街」の間での最小カットを求めている。適当に警備員を置く街と経緯便を置かない街を決めたとき、「置く街」と「置かない街」の間にある辺が、対立の数である。これを最小化する(=最小カットを求める)には、最大流を求めればよい。
コード
#include <queue> #include <algorithm> #include <vector> #include <string> using namespace std; class dinic { struct edge { edge *next, *pair; int src, dest, cap, flow; }; int v, e; edge *pool, **con; int top; int* level; public: dinic(int _v, int _e) { v = _v; e = _e; pool = new edge[e]; con = new edge*[v]; for(int i=0;i<v;i++) con[i] = NULL; top = 0; } ~dinic() { delete [] pool; delete [] con; } edge* _add_edge(int src, int dest, int cap) { edge* ed = &(pool[top++]); ed->src = src; ed->dest = dest; ed->cap = cap; ed->flow = 0; ed->next = con[src]; con[src] = ed; return ed; } void add_pair(int src, int dest, int cap1, int cap2) { edge *e1 = _add_edge(src, dest, cap1); edge *e2 = _add_edge(dest, src, cap2); e1->pair = e2; e2->pair = e1; } int dfs(int s, int d, int cap) { if(s==d || cap==0) return cap; int w = 0; for(edge* ed = con[s]; ed != NULL; ed = ed->next) if(level[ed->dest] > level[s]){ int f = dfs(ed->dest, d, std::min(cap - w, ed->cap - ed->flow)); if(f > 0){ ed->flow += f; ed->pair->flow -= f; w += f; } } return w; } long long max_flow(int s, int d) { level = new int[v]; for(int i=0;i<e;i++) pool[i].flow = 0; long long ret = 0; for(bool update=true; update; ){ update = false; //derminate distance std::fill(level, level+v, -1); level[s] = 0; std::queue<int> q; q.push(0); int ps; while(!q.empty()){ ps = q.front(); q.pop(); for(edge* ed = con[ps]; ed != NULL; ed = ed->next){ if(ed->cap > ed->flow && level[ed->dest]==-1){ level[ed->dest] = level[ps] + 1; q.push(ed->dest); } } } for(;;){ int gain = dfs(s, d, 0x7FFFFFFF); if(gain==0) break; ret += gain; update = true; } } delete [] level; return ret; } }; class NowhereLand { int C, K; bool guard[50][50], agency[50][50], rel[50][50]; vector<int> parse(string s) { vector<int> ret; int p = -1; for(int i=0;i<s.size();i++){ if(s[i]==' '){ ret.push_back(p); p = -1; }else{ if(p==-1) p = 0; else p *= 10; p += s[i]-'0'; } } if(p!=-1) ret.push_back(p); return ret; } int solve(int p) { dinic dn(52, 2600); for(int i=0;i<C;i++) for(int j=i+1;j<C;j++) if(rel[i][j]){ dn.add_pair(i+2, j+2, 1, 1); } for(int i=0;i<C;i++){ if(agency[i][p]&&!guard[i][p]) continue; if(!agency[i][p]) dn.add_pair(0, i+2, 1000, 0); if(guard[i][p]) dn.add_pair(i+2, 1, 1000, 0); } return dn.max_flow(0, 1); } public: int placeGuards(vector<string> cities, int k, vector<string> guards, vector<string> agencies) { C = cities.size(); K = k; for(int i=0;i<C;i++) for(int j=0;j<C;j++) rel[i][j] = (cities[i][j]=='1'); for(int i=0;i<C;i++){ vector<int> g = parse(guards[i]), a = parse(agencies[i]); for(int j=0;j<k;j++) guard[i][j] = agency[i][j] = false; for(int j=0;j<g.size();j++) guard[i][g[j]] = true; for(int j=0;j<a.size();j++) agency[i][a[j]] = true; } int ret = 0; for(int i=0;i<K;i++) ret += solve(i); return ret; } };