加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

POJ PKU 2125 图论 二分图最小点权覆盖

(2010-08-09 15:52:37)
标签:

poj

pku

2125

图论

点权覆盖

it

分类: 网络流

题目描述:

在一个有向图中,对于每个点,有两个值,删除这个点入度的边,代价为w+,删除出度的边,代价为w-
用最小的代价移走所有的边,问你最小的代价和具体的删除方式。

题目描述:

二分图最小点权覆盖

对于n个点,拆点。分别为1~n和n+1~2n

一个超级源点s,和1~n连接,容量为w-

一个超级汇点t,和n+1~2n连接,容量为w+

对于边ab,连接a到b+n,容量无穷大。

这样求得的最大流值就是最小权值。

证明:
割的性质是不存在一条从s到t的路径。
故对于每一条边a和b的路径:s-a-b-t中至少有一条边在割集中。
而ab又是无限大,所以它不会在割集中,则sa或者bt至少一个在最小割中,正好和题意相符合。

然后是找一个符合最优解得最小割集。

 

任意最小割。首先对网络求一次最大流,再对求完最大流的残余网络求一次FloodFill,访问到的点属于S集合,没有访问到的点属于T集合。则从S集合到T集合的边即为最小割集。

 

代码如下:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define size 500
#define Max 0x7fffffff
int cap[size][size],s,t,n;
int queue[size],head,tail, Flow,prev[size];
bool findload()      //bfs找增广路径
{
 int i, tmp;
 memset(prev,-1,sizeof(prev));
 head = tail = 0;;
 queue[tail++] = s;
 prev[s] = s;
 while(head < tail)
 {
  tmp = queue[head++];
  for(i = 0; i <= t; i++)
   if(prev[i] == -1 && cap[tmp][i] > 0)
   {
    prev[i] = tmp;
    if (i == t) return true;
    queue[tail++] = i;
   }
 }
 return false;
}
int maxflow()
{
    Flow = 0;
 while(findload())
 {
  int min = Max;
  for(int i = t; i != s; i = prev[i])  //增广路径中的最小可通过流
   if(min > cap[prev[i]][i])
    min = cap[prev[i]][i];
  for(int i = t; i != s; i = prev[i])//调整路径上的流
  {
   cap[prev[i]][i] -= min;
   cap[i][prev[i]] += min;
  }
  Flow += min;   //最大流累加
 }
 return Flow;
}
int m, w, a, b, re[size];
int main()
{
    scanf("%d%d", &n, &m); memset(cap, 0, sizeof(cap));
    s = 0, t = (n * 2) + 1;
    for(int i = 1; i <= n; i++){scanf("%d", &w);cap[i + n][t] = w;}
    for(int i = 1; i <= n; i++){scanf("%d", &w);cap[s][i] = w;}
    for(int i = 0; i < m; i++) {scanf("%d%d", &a, &b);cap[a][b + n] = Max;}
    int ans = maxflow(), cnt = 0;
    for(int i = 1; i <= t; i++)
    {
        if (prev[i] == -1 && i <= n) cnt++;
        if (prev[i] != -1 && i > n) cnt++;
    }printf("%d\n%d\n", ans, cnt);
    for(int i = 1; i <= t; i++)
    {
        if (prev[i] != -1 && i > n) printf("%d +\n", i - n);
        if (prev[i] == -1 && i <= n) printf("%d -\n", i);
    }
    return 0;
}

 

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有