紫书 习题 11-4 UVa 1660 (网络流拆点法)
这道题改了两天……
因为这道题和节点有关, 所以就用拆点法解决节点的容量问题。
节点拆成两个点, 连一条弧容量为1, 表示只能经过一次。
然后图中的弧容量无限。
然后求最小割, 即最大流, 即为答案。
固定一个源点, 然后枚举汇点, 然后求最小的最小割就ok了。
这里的拆点法连边的时候是拆出来的点连上原来的点。
同时起点是起点拆出来的点终点是原来的点, 因为这起点和终点是可以经过很多次
的。
所以总结一下拆点法(解决每个节点只能经过一次的问题)
(1)开始初始化每个点拆成两个点, 连一条弧, 容量为1
(2)连图中的变得时候拆出来的点连接原来的点, 容量无限
(3)求最大流时起点为原来起点的拆出来的点, 终点为本身
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std;
const int MAXN = 1123;
struct Edge
{
int from, to, cap, flow;
Edge(int from, int to, int cap, int flow) : from(from), to(to), cap(cap), flow(flow) {};
};
vector<Edge> edges;
vector<int> g[MAXN];
int h[MAXN], cur[MAXN];
int n, m, s, t;
void AddEdge(int from, int to, int cap)
{
edges.push_back(Edge(from, to, cap, 0));
edges.push_back(Edge(to, from, 0, 0));
g[from].push_back(edges.size() - 2);
g[to].push_back(edges.size() - 1);
}
bool bfs()
{
queue<int> q;
memset(h, 0, sizeof(h));
h[s] = 1;
q.push(s);
while(!q.empty())
{
int x = q.front(); q.pop();
REP(i, 0, g[x].size())
{
Edge& e = edges[g[x][i]];
if(e.cap > e.flow && !h[e.to])
{
h[e.to] = h[x] + 1;
q.push(e.to);
}
}
}
return h[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(h[x] + 1 == h[e.to] && (f = dfs(e.to, min(a, e.cap - e.flow))) > 0)
{
flow += f;
edges[g[x][i] ^ 1].flow -= f;
e.flow += f;
if((a -= f) == 0) break;
}
}
return flow;
}
int maxflow()
{
int ret = 0;
while(bfs()) memset(cur, 0, sizeof(cur)), ret += dfs(s, 1e9);
return ret;
}
int main()
{
while(~scanf("%d%d", &n, &m))
{
edges.clear();
REP(i, 0, MAXN) g[i].clear();
REP(i, 0, n) AddEdge(i, i + n, 1);
while(m--)
{
int u, v;
scanf(" (%d,%d)", &u, &v);
AddEdge(u + n, v, 1e9); AddEdge(v + n, u, 1e9);
}
int ans = n; s = n;
for(t = 1; t < n; t++)
{
REP(i, 0, edges.size()) edges[i].flow = 0;
ans = min(ans, maxflow());
}
printf("%d\n", ans);
}
return 0;
}