poj 2125 最小割解决 "最小点权覆盖问题" +输出解(割边集)

来源:互联网 发布:unix环境高级编程第3版 编辑:程序博客网 时间:2024/06/06 14:04
 

poj 2125 最小割解决 "最小点权覆盖问题" +输出解(割边集)

分类: ACM_图论_二分匹配 ACM_图论_网络流 ACM_图论_最小割 ACM_图论 356人阅读 评论(0) 收藏 举报
最小割二分匹配

题意:给你一幅有向图, 对于点i删除所有进入该点的边就要支付费用W[i]+(情况1), 删除所有从该点出发的边就要支付费用W[i]-,问删除图中的所有边至少需要多少费用(情况2)。

分析:首先我们根据题意,选点就能删除一些边, 那么这可以看成是“用点去覆盖边”, 这里无非是把边分成了2类,

我们可以把原来的点进行拆点,那么就完完全全等价于“用点去覆盖边",如果支付费用都为1,那么这就是”最小点覆盖集“问题,但这题费用不确定,那么这就是“最小点权覆盖集”问题, 借助二分匹配的思想,我们可以引入“最小割”来解决“最小点权覆盖”问题。

建图:拆点,左点阵为情况2的点, 右点阵为情况1的点,右点阵跟汇点T连流量为W+,左点阵跟源点S连费用为W-,

对于输入的边<u, v> 连边 (u, v+n)费用为无穷大inf。跑一边最大流,求出最小费用。

输出解:最要我们找到一个满足条件的割边集(注意不是所有割边, 因为有一条流已经经过了一条割边,那么下面一条割边就不用选了,这样费用才是最小的),那么就能输出解了。怎么找出割边呢?我们可以在残余网络里走流,如果有一条边是割边,那么之后就流不过去了,不是割边还能继续流,具体实现我们可以从源点S用dfs搜出能走到的点标记vis[] =1,

那么对于边<u,v> 只要 vis[u] = 1 && vis[v] = 0 那就是割边了。

总结:二分匹配的题都可以用最大流来解,在二分图中 有 “最小点覆盖集”和“最打独立集”,如果有了点权,那么就要用最大流(最小割)来解决 “最小点权覆盖集”(最小割)和“最大点权独立集”(最大流)问题。

[cpp] view plaincopy
  1. #include <cstdio>  
  2. #include <cstring>  
  3. #include <algorithm>  
  4. using namespace std;  
  5. const int maxn = 206;  
  6. const int maxm = 10404;  
  7. const int inf = 1e9;  
  8. int n, m, N, S, T;  
  9. struct Edge {  
  10.     int v, c, next;  
  11.     Edge(int v, int c, int next) :  
  12.             v(v), c(c), next(next) {  
  13.     }  
  14.     Edge() {  
  15.     }  
  16. } edge[maxm];  
  17. int head[maxn], E;  
  18. void add(int s, int t, int c) {  
  19.     edge[E] = Edge(t, c, head[s]);  
  20.     head[s] = E++;  
  21.     edge[E] = Edge(s, 0, head[t]);  
  22.     head[t] = E++;  
  23. }  
  24. void init() {  
  25.     memset(head, -1, sizeof(head));  
  26.     E = 0;  
  27. }  
  28. int gap[maxn], dis[maxn], pre[maxn], cur[maxn];  
  29. int sap(int s, int t, int n) // s 源点,t汇点,n顶点总数  
  30.         {  
  31.     int i;  
  32.     for(i = 0; i <= n; i++) {  
  33.         dis[i] = gap[i] = 0;  
  34.         cur[i] = head[i];  
  35.     }  
  36.     gap[0] = n;  
  37.     int u = pre[s] = s, maxf = 0, aug = inf, v;  
  38.     while(dis[s] < n) {  
  39.         loop: for(i = cur[u]; ~i; i = edge[i].next) {  
  40.             v = edge[i].v;  
  41.             if(edge[i].c  && dis[u] == dis[v] + 1) {  
  42.                 aug = min(aug, edge[i].c);  
  43.                 pre[v] = u;  
  44.                 cur[u] = i;  
  45.                 u = v;  
  46.                 if(u == t) {  
  47.                     while(u != s) {  
  48.                         u = pre[u];  
  49.                         edge[cur[u]].c -= aug;  
  50.                         edge[cur[u] ^ 1].c += aug;  
  51.                     }  
  52.                     maxf += aug;  
  53.                     aug = inf;  
  54.                 }  
  55.                 goto loop;  
  56.             }  
  57.         }  
  58.         int d = n;  
  59.         for(i = head[u]; ~i; i = edge[i].next) {  
  60.             v = edge[i].v;  
  61.             if(edge[i].c && dis[v] < d) {  
  62.                 d = dis[v];  
  63.                 cur[u] = i;  
  64.             }  
  65.         }  
  66.         if(!(--gap[dis[u]]))  
  67.             break;  
  68.         ++gap[dis[u] = d + 1];  
  69.         u = pre[u];  
  70.     }  
  71.     return maxf;  
  72. }  
  73. bool vis[maxn];  
  74. void dfs(int u) {  
  75.     vis[u] = 1;  
  76.     for(int i = head[u]; ~i; i = edge[i].next) {  
  77.         int v = edge[i].v;  
  78.         if(!vis[v] && edge[i].c)  
  79.             dfs(v);  
  80.     }  
  81. }  
  82. int cnt, res[maxn];  
  83. void debug() {  
  84.     int i;  
  85.     for(i = head[7];  ~i; i = edge[i].next)  
  86.         printf("v = %d\n", edge[i].v);  
  87. }  
  88. int main() {  
  89.     int i;  
  90.     while(~scanf("%d%d", &n, &m)) {  
  91.         N = n << 1;  
  92.         init();  
  93.         S = 0;  
  94.         T = N+1;  
  95.         int x, y;  
  96.         for(i = 1; i <= n; i++) {  
  97.             scanf("%d", &x);  
  98.             add(i+n, T, x);  
  99.         }  
  100.         for(i = 1; i <= n; i++) {  
  101.             scanf("%d", &x);  
  102.             add(S, i, x);  
  103.         }  
  104.         while(m--) {  
  105.             scanf("%d%d", &x, &y);  
  106.             add(x, y + n, inf);  
  107.         }  
  108.         printf("%d\n", sap(S, T, T+1));  
  109.   
  110.         //dfs  
  111.         memset(vis, 0, sizeof(vis));  
  112.         dfs(S);  
  113.         //枚举所有可能是割边的边  (与S或与T连的边)  
  114.         cnt = 0;  
  115.         for(i = head[S]; ~i; i = edge[i].next) {  
  116.             int v = edge[i].v;  
  117.   
  118.             if(vis[S] && !vis[v]) res[cnt++] = v;  
  119.         }  
  120.         for(i = head[T]; ~i; i = edge[i].next) {  
  121.             int v = edge[i].v;  
  122.             if(vis[v] && !vis[T]) res[cnt++] = v;  
  123.         }  
  124.         printf("%d\n", cnt);  
  125.         for(i = 0; i < cnt; i++)  
  126.             if(res[i] <= n) printf("%d -\n", res[i]);  
  127.             else printf("%d +\n", res[i]-n);  
  128.     }  
  129.     return 0;  
  130. }  
0 0
原创粉丝点击