一道神奇的图论题,给出一个含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());
}
}