【Algorithm】平面图最小割转最短路
杭电上碰巧有几道求最小割的题目,用网络流解超时。
通过离散数学中的一些知识可以将平面图最小割转化为最短路径,通过最短路解提高效率。
这个转化过程很简单,但是很巧妙,详细内容可以参考《浅析最大最小定理在信息学竞赛中的应用》。
1. 【HDU】 3870 Catch the Theves
有一个网格拓扑,每条边都表示有$A_{ij}$个小偷,现在希望对其中一条边部署警察,使得小偷不可能偷到右下角的财宝。
求至少需要多少个警察?
这题是个挺有实际意义的题目,基本思路也很简单。
因为题目给定小偷都从左上角出发向右下角前进。显然可以对网格拓扑建网络流,然后解这个网络流,最小割就是所求解。
示意图如下:
$n \in [1, 400]$直接跑个网络流会超时,因此,我们构建对偶图,将问题转化。
实际,我们应该建立的是原图的对偶图,但是因为对偶图存在环,并且每个环都代表了一个割。
因此我们将环切开,将其分割为两个节点$st$和$ed$,故将问题转化为SSSP问题。新的示意图如下:
故,所有的难点就变成了如何建图。
1 /* 3870 */ 2 #include <iostream> 3 #include <sstream> 4 #include <string> 5 #include <map> 6 #include <queue> 7 #include <set> 8 #include <stack> 9 #include <vector> 10 #include <deque> 11 #include <bitset> 12 #include <algorithm> 13 #include <cstdio> 14 #include <cmath> 15 #include <ctime> 16 #include <cstring> 17 #include <climits> 18 #include <cctype> 19 #include <cassert> 20 #include <functional> 21 #include <iterator> 22 #include <iomanip> 23 using namespace std; 24 //#pragma comment(linker,"/STACK:102400000,1024000") 25 26 #define sti set<int> 27 #define stpii set<pair<int, int> > 28 #define mpii map<int,int> 29 #define vi vector<int> 30 #define pii pair<int,int> 31 #define vpii vector<pair<int,int> > 32 #define rep(i, a, n) for (int i=a;i<n;++i) 33 #define per(i, a, n) for (int i=n-1;i>=a;--i) 34 #define clr clear 35 #define pb push_back 36 #define mp make_pair 37 #define fir first 38 #define sec second 39 #define all(x) (x).begin(),(x).end() 40 #define SZ(x) ((int)(x).size()) 41 #define lson l, mid, rt<<1 42 #define rson mid+1, r, rt<<1|1 43 44 typedef struct { 45 int v, w, nxt; 46 } edge_t; 47 48 const int INF = 0x3f3f3f3f; 49 const int maxn = 405; 50 const int maxv = 160005; 51 const int st = maxv - 1; 52 const int ed = maxv - 2; 53 const int maxe = maxv * 4; 54 int head[maxv], l; 55 edge_t E[maxe]; 56 int dis[maxv]; 57 bool visit[maxv]; 58 int cost[maxn][maxn]; 59 int n; 60 61 void init() { 62 memset(head, -1, sizeof(head)); 63 l = 0; 64 } 65 66 void addEdge(int u, int v, int w) { 67 E[l].v = v; 68 E[l].w = w; 69 E[l].nxt = head[u]; 70 head[u] = l++; 71 } 72 73 void spfa() { 74 queue<int> Q; 75 int u, v, k; 76 77 memset(visit, false, sizeof(visit)); 78 memset(dis, INF, sizeof(dis)); 79 Q.push(st); 80 visit[st] = true; 81 dis[st] = 0; 82 83 while (!Q.empty()) { 84 u = Q.front(); 85 Q.pop(); 86 visit[u] = false; 87 for (k=head[u]; k!=-1; k=E[k].nxt) { 88 v = E[k].v; 89 if (dis[v] > dis[u]+E[k].w) { 90 dis[v] = dis[u] + E[k].w; 91 if (!visit[v]) { 92 visit[v] = true; 93 Q.push(v); 94 } 95 } 96 } 97 } 98 } 99 100 void solve() { 101 init(); 102 int n_ = n - 1; 103 104 rep(i, 0, n) { 105 rep(j, 0, n) { 106 if (i==0 && j!=n-1) { 107 addEdge(st, i*n_+j, cost[i][j]); 108 } 109 if (j==n-1 && i!=n-1) { 110 addEdge(st, i*n_+j-1, cost[i][j]); 111 } 112 if (j==0 && i!=n-1) { 113 addEdge(i*n_+j, ed, cost[i][j]); 114 } 115 if (i==n-1 && j!=n-1) { 116 addEdge((i-1)*n_+j, ed, cost[i][j]); 117 } 118 119 if (i!=n-1 && j!=n-1) { 120 if (i) { 121 addEdge((i-1)*n_+j, i*n_+j, cost[i][j]); 122 addEdge(i*n_+j, (i-1)*n_+j, cost[i][j]); 123 } 124 if (j) { 125 addEdge(i*n_+j-1, i*n_+j, cost[i][j]); 126 addEdge(i*n_+j, i*n_+j-1, cost[i][j]); 127 } 128 } 129 } 130 } 131 132 spfa(); 133 printf("%d\n", dis[ed]); 134 } 135 136 int main() { 137 ios::sync_with_stdio(false); 138 #ifndef ONLINE_JUDGE 139 freopen("data.in", "r", stdin); 140 freopen("data.out", "w", stdout); 141 #endif 142 143 int t; 144 145 scanf("%d", &t); 146 while (t--) { 147 scanf("%d", &n); 148 rep(i, 0, n) 149 rep(j, 0, n) 150 scanf("%d", &cost[i][j]); 151 solve(); 152 } 153 154 #ifndef ONLINE_JUDGE 155 printf("time = %d.\n", (int)clock()); 156 #endif 157 158 return 0; 159 }
2. 【HDU】 3860 Circuit Board
基本方式类似,建图的trick更麻烦一点。
1 /* 3860 */ 2 #include <iostream> 3 #include <sstream> 4 #include <string> 5 #include <map> 6 #include <queue> 7 #include <set> 8 #include <stack> 9 #include <vector> 10 #include <deque> 11 #include <bitset> 12 #include <algorithm> 13 #include <cstdio> 14 #include <cmath> 15 #include <ctime> 16 #include <cstring> 17 #include <climits> 18 #include <cctype> 19 #include <cassert> 20 #include <functional> 21 #include <iterator> 22 #include <iomanip> 23 using namespace std; 24 //#pragma comment(linker,"/STACK:102400000,1024000") 25 26 #define sti set<int> 27 #define stpii set<pair<int, int> > 28 #define mpii map<int,int> 29 #define vi vector<int> 30 #define pii pair<int,int> 31 #define vpii vector<pair<int,int> > 32 #define rep(i, a, n) for (int i=a;i<n;++i) 33 #define per(i, a, n) for (int i=n-1;i>=a;--i) 34 #define clr clear 35 #define pb push_back 36 #define mp make_pair 37 #define fir first 38 #define sec second 39 #define all(x) (x).begin(),(x).end() 40 #define SZ(x) ((int)(x).size()) 41 #define lson l, mid, rt<<1 42 #define rson mid+1, r, rt<<1|1 43 44 #define UP 0 45 #define DOWN 1 46 #define LEFT 2 47 #define RIGHT 3 48 49 typedef struct { 50 int v, w, nxt; 51 } edge_t; 52 53 const int INF = 0x3f3f3f3f; 54 const int maxn = 205; 55 const int maxv = maxn * maxn; 56 const int st = maxv - 1; 57 const int ed = maxv - 2; 58 const int maxe = maxv * 20; 59 int head[maxv], l; 60 edge_t E[maxe]; 61 int dis[maxv]; 62 bool visit[maxv]; 63 pii pp[maxn], op[maxn]; 64 int pn, on; 65 int cap[maxv][4]; 66 int dir[4][2] = { 67 -1,0,1,0,0,-1,0,1 68 }; 69 int ID[maxn][maxn]; 70 int W[10005], wn; 71 int n, m; 72 73 inline bool judge(int x, int y) { 74 return x>=0 && x<n && y>=0 && y<m; 75 } 76 77 int getDir(int x, int y, int xx, int yy) { 78 rep(k, 0, 4) { 79 if (xx==x+dir[k][0] && yy==y+dir[k][1]) 80 return k; 81 } 82 return -1; 83 } 84 85 void init() { 86 memset(head, -1, sizeof(head)); 87 l = 0; 88 } 89 90 void addEdge(int u, int v, int w) { 91 #ifndef ONLINE_JUDGE 92 printf("u = %d, v = %d, w = %d\n", u, v, w); 93 #endif 94 E[l].v = v; 95 E[l].w = w; 96 E[l].nxt = head[u]; 97 head[u] = l++; 98 99 E[l].v = u; 100 E[l].w = w; 101 E[l].nxt = head[v]; 102 head[v] = l++; 103 } 104 105 void spfa() { 106 queue<int> Q; 107 int u, v, k; 108 109 memset(visit, false, sizeof(visit)); 110 memset(dis, -1, sizeof(dis)); 111 dis[st] = 0; 112 visit[st] = true; 113 Q.push(st); 114 115 while (!Q.empty()) { 116 u = Q.front(); 117 Q.pop(); 118 visit[u] = false; 119 for (k=head[u]; k!=-1; k=E[k].nxt) { 120 v = E[k].v; 121 if (dis[v]==-1 || dis[v]>dis[u]+E[k].w) { 122 dis[v] = dis[u]+E[k].w; 123 if (!visit[v]) { 124 visit[v] = true; 125 Q.push(v); 126 } 127 } 128 } 129 } 130 } 131 132 void Build(int bound) { 133 int prel = n * m, prer = prel + pn-1; 134 135 init(); 136 137 // handle power station 138 if (pn > 1) { 139 rep(i, 0, pn) { 140 if (i == 0) { 141 addEdge(st, prel, pp[i].sec); 142 } else if (i == pn-1) { 143 addEdge(prel+pn-2, ed, pp[i].sec); 144 } else { 145 addEdge(prel+i-1, prel+i, pp[i].sec); 146 147 int l = pp[i-1].fir; 148 int r = pp[i].fir; 149 rep(j, l, r) { 150 int tmp = min(cap[ID[j][0]][DOWN], bound); 151 addEdge(prel+i-1, ID[j][0], tmp); 152 } 153 } 154 } 155 } else { 156 addEdge(st, ed, pp[0].sec); 157 } 158 159 // handle terminal station 160 if (on > 1) { 161 rep(i, 0, on) { 162 if (i == 0) { 163 addEdge(st, prer, op[i].sec); 164 } else if (i == on-1) { 165 addEdge(prer+on-2, ed, op[i].sec); 166 } else { 167 addEdge(prer+i-1, prer+i, op[i].sec); 168 169 int l = op[i-1].fir; 170 int r = op[i].fir; 171 rep(j, l, r) { 172 int tmp = min(cap[ID[j][m-1]][DOWN], bound); 173 addEdge(prer+i-1, ID[j][m-2], tmp); 174 } 175 } 176 } 177 } else { 178 addEdge(st, ed, op[0].sec); 179 } 180 181 rep(j, 0, m-1) { 182 int tmp = min(cap[ID[0][j]][RIGHT], bound); 183 addEdge(st, ID[0][j], tmp); 184 185 tmp = min(cap[ID[n-1][j]][RIGHT], bound); 186 addEdge(ed, ID[n-2][j], tmp); 187 } 188 189 rep(i, 0, pp[0].fir) { 190 int tmp = min(cap[ID[i][0]][DOWN], bound); 191 addEdge(st, ID[i][0], tmp); 192 } 193 rep(i, 0, op[0].fir) { 194 int tmp = min(cap[ID[i][m-1]][DOWN], bound); 195 addEdge(st, ID[i][m-2], tmp); 196 } 197 rep(i, pp[pn-1].fir, n-1) { 198 int tmp = min(cap[ID[i][0]][DOWN], bound); 199 addEdge(ID[i][0], ed, tmp); 200 } 201 rep(i, op[on-1].fir, n-1) { 202 int tmp = min(cap[ID[i][m-1]][DOWN], bound); 203 addEdge(ID[i][m-1], ed, tmp); 204 } 205 206 rep(i, 0, n-1) { 207 rep(j, 0, m-1) { 208 int tmp[4]; 209 210 tmp[UP] = min(cap[ID[i][j]][RIGHT], bound); 211 tmp[LEFT] = min(cap[ID[i][j]][DOWN], bound); 212 tmp[DOWN] = min(cap[ID[i+1][j+1]][LEFT], bound); 213 tmp[RIGHT] = min(cap[ID[i+1][j+1]][UP], bound); 214 215 rep(k, 0, 4) { 216 int ii = i + dir[k][0]; 217 int jj = j + dir[k][1]; 218 219 if (ii>=0 && ii<n-1 && jj>=0 && jj<m-1) 220 addEdge(ID[i][j], ID[ii][jj], tmp[k]); 221 } 222 } 223 } 224 225 #ifndef ONLINE_JUDGE 226 putchar('\n'); 227 #endif 228 } 229 230 void solve() { 231 int l = 0, r = wn - 1, mid; 232 int ans = -1; 233 int tot = 0; 234 235 rep(i, 0, on) 236 tot += op[i].sec; 237 238 while (l <= r) { 239 mid = (l + r) >> 1; 240 #ifndef ONLINE_JUDGE 241 // printf("bound = %d\n", W[mid]); 242 #endif 243 Build(W[mid]); 244 spfa(); 245 if (dis[ed] >= tot) { 246 ans = W[mid]; 247 r = mid - 1; 248 } else { 249 l = mid + 1; 250 } 251 } 252 253 printf("%d\n", ans); 254 } 255 256 int main() { 257 ios::sync_with_stdio(false); 258 #ifndef ONLINE_JUDGE 259 freopen("data.in", "r", stdin); 260 freopen("data.out", "w", stdout); 261 #endif 262 263 int t; 264 265 scanf("%d", &t); 266 while (t--) { 267 scanf("%d%d", &n,&m); 268 scanf("%d", &pn); 269 270 // power station & terminal station 271 rep(i, 0, pn) { 272 scanf("%d%d", &pp[i].fir, &pp[i].sec); 273 --pp[i].fir; 274 } 275 scanf("%d", &on); 276 rep(i, 0, on) { 277 scanf("%d%d", &op[i].fir, &op[i].sec); 278 --op[i].fir; 279 } 280 sort(pp, pp+pn); 281 sort(op, op+on); 282 283 // cap between adjancy points 284 int id = 0; 285 rep(i, 0, n) { 286 rep(j, 0, m) { 287 rep(k, 0, 4) { 288 if (judge(i+dir[k][0], j+dir[k][1])) 289 cap[id][k] = INF; 290 else 291 cap[id][k] = 0; 292 } 293 ID[i][j] = id++; 294 } 295 } 296 297 // cap has boundry 298 int q; 299 int x1, y1, x2, y2, tmp; 300 scanf("%d", &q); 301 while (q--) { 302 scanf("%d%d%d%d%d", &x1,&y1,&x2,&y2,&tmp); 303 --x1; --y1; --x2; --y2; 304 cap[ID[x1][y1]][getDir(x1,y1,x2,y2)] = tmp; 305 cap[ID[x2][y2]][getDir(x2,y2,x1,y1)] = tmp; 306 } 307 308 // cap with destroyed point 309 scanf("%d", &q); 310 while (q--) { 311 scanf("%d%d", &x1,&y1); 312 --x1, --y1; 313 rep(k, 0, 4) { 314 x2 = x1 + dir[k][0]; 315 y2 = y1 + dir[k][1]; 316 if (judge(x2, y2)) 317 cap[ID[x2][y2]][getDir(x2,y2,x1,y1)] = 0; 318 } 319 } 320 321 // wire kind 322 scanf("%d", &wn); 323 rep(i, 0, wn) 324 scanf("%d", &W[i]); 325 sort(W, W+wn); 326 327 solve(); 328 } 329 330 #ifndef ONLINE_JUDGE 331 printf("time = %d.\n", (int)clock()); 332 #endif 333 334 return 0; 335 }
3. 【HDU】 3035 War
这个和3870很类似,建图稍微复杂一点儿,强烈建议做3860,建图最为复杂。
示意图如下:
1 /* 3035 */ 2 #include <iostream> 3 #include <sstream> 4 #include <string> 5 #include <map> 6 #include <queue> 7 #include <set> 8 #include <stack> 9 #include <vector> 10 #include <deque> 11 #include <bitset> 12 #include <algorithm> 13 #include <cstdio> 14 #include <cmath> 15 #include <ctime> 16 #include <cstring> 17 #include <climits> 18 #include <cctype> 19 #include <cassert> 20 #include <functional> 21 #include <iterator> 22 #include <iomanip> 23 using namespace std; 24 //#pragma comment(linker,"/STACK:102400000,1024000") 25 26 #define sti set<int> 27 #define stpii set<pair<int, int> > 28 #define mpii map<int,int> 29 #define vi vector<int> 30 #define pii pair<int,int> 31 #define vpii vector<pair<int,int> > 32 #define rep(i, a, n) for (int i=a;i<n;++i) 33 #define per(i, a, n) for (int i=n-1;i>=a;--i) 34 #define clr clear 35 #define pb push_back 36 #define mp make_pair 37 #define fir first 38 #define sec second 39 #define all(x) (x).begin(),(x).end() 40 #define SZ(x) ((int)(x).size()) 41 #define lson l, mid, rt<<1 42 #define rson mid+1, r, rt<<1|1 43 44 typedef struct { 45 int v, w, nxt; 46 } edge_t; 47 48 typedef struct node_t { 49 int v, w; 50 51 node_t() {} 52 node_t(int v, int w): 53 v(v), w(w) {} 54 55 friend bool operator< (const node_t& a, const node_t& b) { 56 return a.w > b.w; 57 } 58 59 } node_t; 60 61 const int INF = 0x3f3f3f3f; 62 const int maxv = 1e6+15; 63 const int maxe = 5e6+15; 64 const int st = maxv - 1; 65 const int ed = maxv - 2; 66 int head[maxv], l; 67 bool visit[maxv]; 68 int dis[maxv]; 69 edge_t E[maxe]; 70 int n, m; 71 72 void init() { 73 l = 0; 74 memset(head, -1, sizeof(head)); 75 } 76 77 void addEdge(int u, int v, int w) { 78 #ifndef ONLINE_JUDGE 79 // printf("u = %d, v = %d, w = %d\n", u, v, w); 80 #endif 81 E[l].v = v; 82 E[l].w = w; 83 E[l].nxt = head[u]; 84 head[u] = l++; 85 86 E[l].v = u; 87 E[l].w = w; 88 E[l].nxt = head[v]; 89 head[v] = l++; 90 } 91 92 void spfa() { 93 priority_queue<node_t> Q; 94 node_t nd; 95 96 memset(visit, false, sizeof(visit)); 97 memset(dis, INF, sizeof(dis)); 98 Q.push(node_t(st, 0)); 99 dis[st] = 0; 100 101 while (!Q.empty()) { 102 nd = Q.top(); 103 Q.pop(); 104 int& u = nd.v; 105 if (u == ed) 106 break; 107 if (visit[u]) 108 continue; 109 110 visit[u] = true; 111 for (int k=head[u]; k!=-1; k=E[k].nxt) { 112 int& v = E[k].v; 113 if (!visit[v] && dis[v]>dis[u]+E[k].w) { 114 dis[v] = dis[u] + E[k].w; 115 Q.push(node_t(v, dis[v])); 116 } 117 } 118 } 119 } 120 121 void Build() { 122 int w, w1, w2; 123 124 rep(j, 0, m) { 125 scanf("%d", &w); 126 addEdge(st, (j<<2)|1, w); 127 } 128 129 rep(i, 0, n-1) { 130 rep(j, 0, m) { 131 scanf("%d", &w); 132 addEdge((i*m+j)<<2|3, ((i+1)*m+j)<<2|1, w); 133 } 134 } 135 136 rep(j, 0, m) { 137 scanf("%d", &w); 138 addEdge(((n-1)*m+j)<<2|3, ed, w); 139 } 140 141 rep(i, 0, n) { 142 scanf("%d", &w); 143 addEdge((i*m)<<2, ed, w); 144 rep(j, 0, m-1) { 145 scanf("%d", &w); 146 addEdge((i*m+j)<<2|2, (i*m+j+1)<<2, w); 147 } 148 scanf("%d", &w); 149 addEdge(st, (i*m+m-1)<<2|2, w); 150 } 151 152 rep(i, 0, n) { 153 rep(j, 0, m) { 154 scanf("%d%d", &w1,&w2); 155 int base = (i*m+j)<<2; 156 addEdge(base, base|1, w1); 157 addEdge(base|1, base|2, w2); 158 } 159 rep(j, 0, m) { 160 scanf("%d%d", &w1,&w2); 161 int base = (i*m+j)<<2; 162 addEdge(base|3, base, w1); 163 addEdge(base|2, base|3, w2); 164 } 165 } 166 } 167 168 void solve() { 169 init(); 170 Build(); 171 spfa(); 172 printf("%d\n", dis[ed]); 173 } 174 175 int main() { 176 ios::sync_with_stdio(false); 177 #ifndef ONLINE_JUDGE 178 freopen("data.in", "r", stdin); 179 freopen("data.out", "w", stdout); 180 #endif 181 182 while (scanf("%d%d", &n, &m)!=EOF) { 183 solve(); 184 } 185 186 #ifndef ONLINE_JUDGE 187 printf("time = %d.\n", (int)clock()); 188 #endif 189 190 return 0; 191 }