Algorithm平面图最小割转最短路
Posted Bombe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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(falseBZOJ1001: [BeiJing2006]狼抓兔子 (最小割转最短路)bzoj 2007 [Noi2010]海拔——最小割转最短路