一.原题链接:http://poj.org/problem?id=2125
二.题目大意:给一个带点权的有向图,(点权:分为进度的权和出度的权)。然后每次可以移去图中的一个点,移去点的同时,可以选择移去进入点的所有边或者由点出发的所有边,但是有相应的花费,比如说点权的进度的权是5,移去所有进入该点的边,花费是5。求花费最少移去所有边。
三.思路:题目要求要移去所有的边,求最小点权。我们可以把原图拆成一个二分图(对应出度和入度),转换为网络流来求。
建图如下:
1.把原来每个点拆为2个点,分别为二分图的2边,如果原图中2点有连线,则二分图2边连线,容量为无穷大。
2.建立一个超级源点和左边每个点相连,容量为其出度的权大小。
3.建立一个超级汇点和右边每个点相连,容量为其入度的权大小。
跑一遍网络最大流,得到的就是最小割。
为什么最小割就是正确的答案呢,我们已经让中间的边为INF,无法被割到,因此割到的只能是左边或者右边的边,对应的就是选择去掉入度或者去掉出度,而且割的定义保证源点到汇点不联通,这样就等于去掉了所有的边。这么说吧,在左边去掉一条边,等于在中间去掉所有由这点出发的边,在右边去掉一条边,等于在中间去掉所有进去这一点的边。比如说对于下图,去掉s->1等于去掉中间的1->1,1->2。去掉1->t,相当于去掉1->1,2->1。
图:构图方法
接下来就是输出最小割了,从s点开始,深度优先搜索,标记搜得到的点。由于被割的只有左边或者右边的边,
所以如果左边的点没有被访问过,说明割边在左边(比如说左边的1没有被访问过,则s->1是割边,也就是输出1 -。)
如果右边有被访问过,说明s能到达,那么肯定不能到达t,而它到t之间只剩下一条边,于是此边是割边。
四.代码:
#include <iostream> #include <cstdio> #include <queue> #include <cstring> #include <cstdlib> using namespace std; const int INF = 0x3f3f3f3f, MAX_N = 300; class Dinic { public: struct Edge { int v, w, next; }; int cnt, head[MAX_N], dist[MAX_N], s, t; Edge edges[MAX_N*MAX_N]; void init(int is, int it) { cnt = 0; s = is, t = it; memset(head, -1, sizeof(head)); } void addEdge(int u, int v, int weight) { edges[cnt] = (Edge){v, weight, head[u]}; head[u] = cnt++; edges[cnt] = (Edge){u, 0, head[v]}; head[v] = cnt++; } bool BFS() { int i, cur; queue <int> que; que.push(s); memset(dist, -1, sizeof(dist)); dist[s] = 0; while(!que.empty()){ cur = que.front(); que.pop(); for(i = head[cur]; i != -1; i = edges[i].next) if(-1 == dist[edges[i].v] && edges[i].w){ dist[edges[i].v] = dist[cur] + 1; que.push(edges[i].v); } } return dist[t] != -1; } int DFS(int start, int curFlow) { if(start == t) return curFlow; int i, minFlow = 0, v, temp; for(i = head[start]; i != -1; i = edges[i].next){ v = edges[i].v; if(dist[start] == dist[v] - 1 && edges[i].w > 0){ temp = DFS(v, min(edges[i].w, curFlow)); edges[i].w -= temp; edges[i^1].w += temp; curFlow -= temp; minFlow += temp; if(0 == curFlow) break; } } if(0 == minFlow) dist[start] = -2; return minFlow; } int maxFlow() { int res = 0; while(BFS()){ res += DFS(s, INF); } return res; } }G; int nodeNum, edgeNum, inWeight[MAX_N], outWeight[MAX_N]; bool mp[MAX_N][MAX_N], visited[MAX_N]; vector <int> ans; void read() { int i, j, u, v; scanf("%d%d", &nodeNum, &edgeNum); for(i = 1; i <= nodeNum; i++) scanf("%d", &inWeight[i]); for(i = 1; i <= nodeNum; i++) scanf("%d", &outWeight[i]); memset(mp, 0, sizeof(mp)); for(i = 1; i <= edgeNum; i++){ scanf("%d%d", &u, &v); mp[u][v] = true; } } void buildG() { int s = 2*nodeNum + 1, t = 2*nodeNum + 2, i, j; G.init(s, t); for(i = 1; i <= nodeNum; i++) G.addEdge(s, i, outWeight[i]); for(i = 1; i <= nodeNum; i++) G.addEdge(i+nodeNum, t, inWeight[i]); for(i = 1; i <= nodeNum; i++) for(j = 1; j <= nodeNum; j++) if(mp[i][j]) G.addEdge(i, j+nodeNum, INF); } void DFS(int u) { visited[u] = true; int i, v; for(i = G.head[u]; i != -1; i = G.edges[i].next){ v = G.edges[i].v; if(G.edges[i].w > 0 && !visited[v]) DFS(v); } } void setAns() { ans.clear(); int i; for(i = 1; i <= nodeNum; i++){ if(!visited[i]) ans.push_back(i); if(visited[i+nodeNum]) ans.push_back(i+nodeNum); } } int main() { //freopen("in.txt", "r", stdin); int i; read(); buildG(); printf("%d\n", G.maxFlow()); memset(visited, 0, sizeof(visited)); DFS(2*nodeNum+1); setAns(); printf("%d\n", ans.size()); for(i = 0; i < ans.size(); i++) if(ans[i] <= nodeNum) printf("%d -\n", ans[i]); else{ printf("%d +\n", (ans[i] - nodeNum)); } }