这道题改了两天……
因为这道题和节点有关, 所以就用拆点法解决节点的容量问题。
节点拆成两个点, 连一条弧容量为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; }