一道神奇的图论题,给出一个含n个点(n < 50), m条边 (m <= 4000),边权为1的有向图,求删除最少点数使得点1到点n不存在距离大于k的路径。 虽然有本书上有个用dfs递归求解的方法,但不太好理解。。。但是!用最大流和费用流都能解,网络流也忒强大了点,这就是传说中的“一切图论皆网络流”么。。。
我先用的费用流:建图:对每个点i拆点为i和i+n,令源点s=1,汇点t=2*n。增加弧(1, 1+n) ,(n, 2*n) 容量都为INF,费用都为0; 而对于非源汇点, 增加弧(i, i+n)容量为1, 费用为0。若原图中存在边(u, v),增加弧(u+n, v), 容量为1,费用为1(边权)。题目要求删点后点1到点n不存在距离大于k的路径,则对该网络求费用流,在费用大于k的时候停止(费用=边权=1).答案便是此时的最大流。
然后又用最大流做了一次:某本图论书上有这样一段话“在图中删除最少点使得源汇点失去连通性,一般用最小割最大流求解”.而对于这个题,显然在原图中本身从1到n的距离大于k的路径是不用考虑的,所以在建图前先要删去这些边。如何删去这些没用的边呢?用floyd!floyd求完所有点对距离后,对于边(u, v),若dist[1][u] + dist[v][n] + 1 <= k,则把这条边加到网络中去。。。建图与费用流基本相似,只是少了费用而已。
费用流代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<vector> #include<queue> using namespace std; const int maxn = 200; const int INF = 1e9; struct Edge { int from, to, cap, flow, cost; }; int n, m, k, s, t, flow, cost; vector<Edge> edges; vector<int> G[maxn]; int inq[maxn], d[maxn], a[maxn], p[maxn]; void init() { for(int i=0; i<=t; i++) G[i].clear(); edges.clear(); } void add(int from, int to, int cap, int cost) { edges.push_back((Edge){from, to, cap, 0, cost}); edges.push_back((Edge){to, from, 0, 0, -cost}); int tmp = edges.size(); G[from].push_back(tmp-2); G[to].push_back(tmp-1); } bool bellmanford(int s, int t, int &flow, int &cost) { for(int i=0; i<=t; i++) d[i] = INF; memset(inq, 0, sizeof(inq)); d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF; queue<int> Q; Q.push(s); while(!Q.empty()) { int u = Q.front(); Q.pop(); inq[u] = 0; for(int i=0; i<G[u].size(); i++) { Edge& e = edges[G[u][i]]; if(e.cap > e.flow && d[e.to] > d[u] + e.cost) { d[e.to] = d[u] + e.cost; p[e.to] = G[u][i]; a[e.to] = min(a[u], e.cap - e.flow); if(!inq[e.to]) { Q.push(e.to); inq[e.to] = 1; } } } } if(d[t] > k) return false;//!!! flow += a[t]; cost += d[t] * a[t]; int u = t; while(u != s) { edges[p[u]].flow += a[t]; edges[p[u]^1].flow -= a[t]; u = edges[p[u]].from; } return true; } int MCMF() { flow = 0, cost = 0; while(bellmanford(s, t, flow, cost)); return flow; //!!! } int main() { while(scanf("%d%d%d", &n, &m, &k) && n+m+k) { s = 1; t =2*n; init(); int u, v; while(m--) { scanf("%d%d", &u, &v); add(u+n, v, 1, 1); } for(int i=2; i<n; i++) add(i, i+n, 1, 0); add(1, 1+n, INF, 0); add(n, 2*n, INF, 0); printf("%d\n", MCMF()); } }
然后是最大流代码:
#include<iostream> #include<algorithm> #include<vector> #include<queue> #include<cstdio> #include<cstring> using namespace std; const int maxn = 200; const int INF = 1e5; struct Edge { int from, to, cap, flow; }; int n, m, k, s, t; vector<Edge> edges; vector<int> G[maxn]; bool vis[maxn]; int d[maxn], cur[maxn]; int map[maxn][maxn], tmap[maxn][maxn]; inline void init() { for(int i=0; i<=t; i++) G[i].clear(); edges.clear(); } void add(int from, int to, int cap) { edges.push_back((Edge){from, to, cap, 0}); edges.push_back((Edge){to, from, 0, 0}); int tmp = edges.size(); G[from].push_back(tmp-2); G[to].push_back(tmp-1); } bool bfs() { memset(vis, 0, sizeof(vis)); queue<int> q; q.push(s); d[s] = 0; vis[s] = 1; while(!q.empty()) { int x = q.front(); q.pop(); for(int i=0; i<G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(!vis[e.to] && e.cap > e.flow) { vis[e.to] = 1; d[e.to] = d[x] + 1; q.push(e.to); } } } return vis[t]; } int dfs(int x, int a) { if(x == t || a == 0) return a; int flow = 0, f; for(int& i=cur[x]; i<G[x].size(); i++) { Edge& e = edges[G[x][i]]; if(d[x]+1 == d[e.to] && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0) { e.flow += f; edges[G[x][i]^1].flow -=f; flow += f; a -= f; if(a == 0) break; } } return flow; } int max_flow() { int flow = 0; while(bfs()) { memset(cur, 0, sizeof(cur)); flow += dfs(s, INF); } return flow; } void floyd() { memcpy(&tmap, &map, sizeof(map)); for(int l=1; l<=n; l++) for(int i=1; i<=n; i++) for(int j=1; j<=n;j++) if(tmap[i][j] > tmap[i][l] + tmap[l][j] && i != j) tmap[i][j] = tmap[i][l] + tmap[l][j]; for(int i=2; i<n; i++) add(i, i+n, 1); add(1, 1+n, INF); add(n, 2*n, INF); for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) { if(map[i][j] == INF) continue; if(tmap[1][i] + tmap[j][n] + 1 > k) continue; add(i+n, j, INF); } } int main() { while(scanf("%d%d%d", &n, &m, &k) && n+m+k) { s = 1; t = 2*n; init(); for(int i=1; i<=n; i++) { for(int j=1; j<=n; j++) map[i][j] = INF; map[i][i] = 0; } int u, v; while(m--) { scanf("%d%d", &u, &v); map[u][v] = 1; } floyd(); printf("%d\n", max_flow()); } }