标签:
Time Limit: 2000MS | Memory Limit: 65536K | |||
Total Submissions: 7597 | Accepted: 2434 | Special Judge |
Description
Input
Output
Sample Input
3 6 1 2 3 4 2 1 1 2 1 1 3 2 1 2 3 1 2 3
Sample Output
5 3 1 + 2 - 2 +
题意:给你一幅有向图, 对于点i删除所有进入该点的边就要支付费用W[i]+(情况1), 删除所有从该点出发的边就要支付费用W[i]-,问删除图中的所有边至少需要多少费用(情况2)。
分析:首先我们根据题意,选点就能删除一些边, 那么这可以看成是“用点去覆盖边”, 这里无非是把边分成了2类,
我们可以把原来的点进行拆点,那么就完完全全等价于“用点去覆盖边",如果支付费用都为1,那么这就是”最小点覆盖集“问题,但这题费用不确定,那么这就是“最小点权覆盖集”问题, 借助二分匹配的思想,我们可以引入“最小割”来解决“最小点权覆盖”问题。
建图:拆点,左点阵为情况2的点, 右点阵为情况1的点,右点阵跟汇点T连流量为W+,左点阵跟源点S连费用W-,
对于输入的边<u, v> 连边 (u, v+n)费用为无穷大inf。跑一边最大流,求出最小费用。
输出解:不理解啊。
void solve(int u){ mark[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].v; if(edge[i].cap - edge[i].flow > 0 && !mark[v]) solve(v); } }
总结:二分匹配的题都可以用最大流来解,在二分图中 有 “最小点覆盖集”和“最打独立集”,如果有了点权,那么就要用最大流(最小割)来解决 “最小点权覆盖集”(最小割)和“最大点权独立集”(最大流)问题。
以上解析来自http://blog.csdn.net/pi9nc/article/details/27112091
看了别人的解析一下午,对于最小割集的那一块还是不懂, 其他大神还都是用sap写的,sap输出解集和dinic的输出解集的代码还不一样, 而且对最小割不够理解,只能一点一点的调试,试了一下午终于试了出。醉了醉了,理论不扎实,只会用模板的诟病。奋斗奋斗,要理解最大流和最小割 。
#include <cstdio> #include <cstring> #include <algorithm> #include <queue> #include <cmath> #define maxn 5500 #define maxm 1000000 #define INF 0x3f3f3f3f using namespace std; int m, n; int head[maxn],cur[maxn], cnt; int dist[maxn], vis[maxn]; int mark[maxn]; struct node{ int u, v, cap, flow, next; }; node edge[maxm]; void init(){ cnt = 0; memset(head, -1, sizeof(head)); memset(mark, 0, sizeof(mark)); } void add(int u, int v, int w){ node E1 = {u, v, w, 0, head[u]}; edge[cnt] = E1; head[u] = cnt++; node E2 = {v, u, 0, 0, head[v]}; edge[cnt] = E2; head[v] = cnt++; } void getmap(){ int a, u ,v; for(int i = 1; i <= n; ++i){ scanf("%d", &a); add(i + n, n * 2 + 1, a); } for(int i = 1; i <= n; ++i){ scanf("%d", &a); add(0, i, a); } while(m--){ scanf("%d%d", &u, &v); add(u, v + n, INF); } } bool BFS(int st ,int ed){ queue<int>q; memset(vis, 0 ,sizeof(vis)); memset(dist, -1, sizeof(dist)); vis[st] = 1; dist[st] = 0; q.push(st); while(!q.empty()){ int u = q.front(); q.pop(); for(int i = head[u]; i != -1; i = edge[i].next){ node E = edge[i]; if(!vis[E.v] && E.cap > E.flow){ vis[E.v] = 1; dist[E.v] = dist[u] + 1; if(E.v == ed) return true; q.push(E.v); } } } return false; } int DFS(int x, int ed, int a){ if(x == ed || a == 0) return a; int flow = 0, f; for(int &i = cur[x]; i != -1; i = edge[i].next){ node &E = edge[i]; if(dist[E.v] == dist[x] + 1 && (f = DFS(E.v, ed, min(a, E.cap - E.flow))) > 0){ E.flow += f; edge[i ^ 1].flow -= f; a -= f; flow += f; if(a == 0) break; } } return flow; } int maxflow(int st, int ed){ int flowsum = 0; while(BFS(st,ed)){ memcpy(cur, head, sizeof(head)); flowsum += DFS(st, ed, INF); } return flowsum; } void solve(int u){ mark[u] = 1; for(int i = head[u]; i != -1; i = edge[i].next){ int v = edge[i].v; if(edge[i].cap - edge[i].flow > 0 && !mark[v]) solve(v); } } int main (){ while(scanf("%d%d", &n, &m) != EOF){ init(); getmap(); printf("%d\n", maxflow(0, 2 * n + 1)); solve(0); int ans= 0; for(int i = 1; i <= n; ++i){ if(!mark[i]) ans++; if(mark[i + n]) ans++; } printf("%d\n", ans); for(int i = 1;i <= n; ++i){ if(!mark[i]) printf("%d -\n", i); if(mark[i + n]) printf("%d +\n", i); } } return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。
POJ 2125 --Destroying The Graph【最小割解决 "最小点权覆盖问题" && 输出解(割边集) && 各种不懂】
标签:
原文地址:http://blog.csdn.net/hpuhjh/article/details/47343439