POJ2125 Destroying The Graph 二分图 + 最小点权覆盖 + 最小割

思路来源:http://blog.csdn.net/lenleaves/article/details/7873441

求最小点权覆盖,同样求一个最小割,但是要求出割去了那些边,

只要用最终的剩余网络进行一次遍历就可以了,比较简单。

建图:同样是一个二分图,左边的点代表去掉出边,

右边的点代表去掉入边(小心别弄混),左边去掉出边的点与源点相连,

容量为wi- 。

然后更据给出的弧进行连线,权值为INF


使用很好理解的EK算法:(360MS)

//#pragma comment(linker, "/STACK:16777216") //for c++ Compiler
#include <stdio.h>
#include <iostream>
#include <climits>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mm(a) memset((a),0,sizeof((a)))
#define ll long long
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 220;

queue <int> que;

int vis[MAXN], res[MAXN], pre[MAXN];
int n, m, map[MAXN][MAXN];
int src, des;
stack <int> ss;

bool bfs(int src, int des){
    int index;
    memset(pre, -1, sizeof(pre));
    while(!que.empty()) que.pop();
    pre[src] = 0;
    que.push(src);
    while(!que.empty()){
        index = que.front();
        que.pop();
        for(int i = src; i <= des; ++i){
            if(pre[i] == -1 && map[index][i] > 0){
                pre[i] = index;
                if(i == des)    return true;
                que.push(i);
            }
        }
    }
    return false;
}

int MaxFlow(int src, int des){
    int i, maxflow = 0;
    while(bfs(src, des)){
        int minflow = INF;
        for(i = des; i != src; i = pre[i])
            minflow = min(minflow, map[pre[i]][i]);
        for(i = des; i != src; i = pre[i]){
            map[pre[i]][i] -= minflow;
            map[i][pre[i]] += minflow;
        }
        maxflow += minflow;
    }
    return maxflow;
}

void init(){
    mm(map);mm(vis);
}

void dfs(int p){
    if(vis[p]) return ;
    vis[p] = true;
    for(int i = src; i < des; ++i)
        if(!vis[i] && map[p][i]) dfs(i);
}

void solve(){
    int i, x, y, ans = 0, temp;
    init();
    for(i = 1; i <= n; ++i)
        scanf("%d",&map[n + i][n * 2 + 1]);
    for(i = 1; i <= n; ++i)
        scanf("%d",&map[0][i]);
    for(i = 1; i <= m; ++i){
        scanf("%d%d",&x,&y);
        map[x][y + n] = INF;
    }

    n = n << 1;
    src = 0, des = n + 1;
    ans = MaxFlow(src, des);

    dfs(0);

    printf("%d\n",ans);
    n = n >> 1;

    for(i = 1; i <= n; ++i){
        if(!vis[i])
            ss.push(i);
        if(vis[i + n])
            ss.push(i + n);
    }
    printf("%d\n",ss.size());
    while(!ss.empty()){
        temp = ss.top();
        ss.pop();
        if(temp <= n)   printf("%d -\n",temp);
        else    printf("%d +\n",temp - n);
    }
}

int main(){
    while(EOF != scanf("%d%d",&n,&m))    solve();
    return 0;
}

使用SAP + GAP 优化:(79MS)

//#pragma comment(linker, "/STACK:16777216") //for c++ Compiler
#include <stdio.h>
#include <iostream>
#include <climits>
#include <cstring>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <algorithm>
#define mm(a) memset((a),0,sizeof((a)))
#define ll long long
using namespace std;

const int INF = 0x3f3f3f3f;
const int MAXN = 220;

int n, m, map[MAXN][MAXN], dis[MAXN], gap[MAXN];
int src, des;
bool vis[MAXN];
stack <int> ss;

void init(){
    mm(dis);mm(gap);mm(map);mm(vis);
}

int sap(int u,int flow){
    if(u == des) return flow;
    int ans = 0, i, t;
    for(i = 0; i <= n + 1; ++i)
        if(map[u][i] && dis[u] == dis[i] + 1){
            t = sap(i, min(flow - ans, map[u][i]));
            map[u][i] -= t, map[i][u] += t,ans += t;
            if(ans == flow) return ans;
        }
    if(dis[src] >= n + 2) return ans;
    if(!--gap[dis[u]]) dis[src] = n + 2;
    ++gap[++dis[u]];
    return ans;
}

void dfs(int p){
    if(vis[p]) return ;
    vis[p] = true;
    for(int i = 0; i < n + 1; ++i)
        if(!vis[i] && map[p][i]) dfs(i);
}

void solve(){
    int i, x, y, ans = 0, temp;
    init();
    for(i = 1; i <= n; ++i)
        scanf("%d",&map[n + i][n * 2 + 1]);
    for(i = 1; i <= n; ++i)
        scanf("%d",&map[0][i]);
    for(i = 1; i <= m; ++i){
        scanf("%d%d",&x,&y);
        map[x][y + n] = INF;
    }

    n = n << 1;
    src = 0, des = n + 1;
    for(gap[0] = n + 2; dis[src] < n + 2; )
        ans += sap(src,INF);

    dfs(0);

    printf("%d\n",ans);
    n = n >> 1;

    for(i = 1; i <= n; ++i){
        if(!vis[i])
            ss.push(i);
        if(vis[i + n])
            ss.push(i + n);
    }
    printf("%d\n",ss.size());
    while(!ss.empty()){
        temp = ss.top();
        ss.pop();
        if(temp <= n)   printf("%d -\n",temp);
        else    printf("%d +\n",temp - n);
    }
}

int main(){
    while(scanf("%d%d",&n,&m)!=EOF)    solve();
    return 0;
}

POJ2125 Destroying The Graph 二分图 + 最小点权覆盖 + 最小割,布布扣,bubuko.com

时间: 2024-10-13 19:53:58

POJ2125 Destroying The Graph 二分图 + 最小点权覆盖 + 最小割的相关文章

poj3308 Paratroopers --- 最小点权覆盖-&gt;最小割

题目是一个很明显的二分图带权匹配模型, 添加源点到nx建边,ny到汇点建边,(nx,ny)=inf建边,求最小割既得最小点权覆盖. 在本题中由于求的是乘积,所以先全部取log转换为加法,最后再乘方回来. #include <iostream> #include <cstring> #include <string> #include <cstdio> #include <cmath> #include <algorithm> #inc

POJ 3308 Paratroopers (二分图最小点权覆盖 -&gt; 最小割 -&gt; 最大流)

POJ 3308 Paratroopers 链接:http://poj.org/problem?id=3308 题意:有一个N*M的方阵,有L个伞兵降落在方阵上.现在要将所有的伞兵都消灭掉,可以在每行每列装一个高射炮,如果在某行(某列)装上高射炮之后,能够消灭所有落在该行(该列)的伞兵.每行每列安高射炮有费用,问如何安装能够使得费用之积最小. 思路:首先题目要求乘积最小,将乘积对e取对数,会发现就变成了求和.然后抽象出一个二分图,每一行是x部的一个点,每个点有权值,权值为费用取ln.每一列是y部

POJ 3308--Paratroopers【 最小点权覆盖 &amp;&amp; 最小割】

Paratroopers Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 7847   Accepted: 2365 Description It is year 2500 A.D. and there is a terrible war between the forces of the Earth and the Mars. Recently, the commanders of the Earth are infor

POJ 2125 Destroying the Graph 二分图最小点权覆盖

Destroying The Graph Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8198   Accepted: 2635   Special Judge Description Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that B

POJ2125 Destroying The Graph (最小点权覆盖集)(网络流最小割)

Destroying The Graph Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8158   Accepted: 2620   Special Judge Description Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that B

POJ2125 Destroying The Graph 最小点权覆盖

题目链接: poj2125 题意: 给出一张N个顶点M条边的有向图. 对于每个顶点x,有两种操作: 1,删除所有进入x的边,花费为a; 2.删除所有从x出去的边,花费为b. 问把图中所有边删除所需要的最小花费.并输出对应的操作. 解题思路: 由题目条件(删除入边,删除出边)首先想到应该是拆点. 这样题目的问题转化为最小点权覆盖问题.即用最少(花费)的顶点覆盖所有边 对于这个问题,我们可以用网络流中的最小割解决,方法如下: 源点连接拆后的出点,容量为b 汇点连接拆后的入点,容量为a 已有边容量为无

POJ 2125 --Destroying The Graph【最小割解决 &quot;最小点权覆盖问题&quot; &amp;&amp; 输出解(割边集) &amp;&amp; 各种不懂】

Destroying The Graph Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7597   Accepted: 2434   Special Judge Description Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that B

【网络流】【最小点权覆盖】【NEERC 2003】【POJ2125】【cogs 1575】有向图破坏

1575. [NEERC 2003][POJ2125]有向图破坏 ★★★ 输入文件:destroyingthegraph.in 输出文件:destroyingthegraph.out 简单对比 时间限制:1 s 内存限制:256 MB [题目描述] Alice和Bob正在玩如下的游戏.首先Alice画一个有N个顶点,M条边的有向图.然后Bob试着摧毁它.在一次操作中他可以找到图中的一个点,并且删除它所有的入边或所有的出边. Alice给每个点定义了两个值:Wi+和Wi-.如果Bob删除了第i个点

二分图最小点权覆盖 二分图最大权独立集 方格取数 最小割

二分图最小点权覆盖: 每一条边 (u, v) 都是一个限制条件, 要求 u 和 v 不能同时取得. 我们考虑先取得所有的, 然后减去最小的点权. 建立原点 S , 连向二分图左边的所有点, 与 S 连通的意义是左边的点被选择了, 或者右边的点没有被选择. 建立汇点 T , 二分图右边的所有点连向它, 与 T 连通的意义是左边的点没有被选择, 或者右边的点被选择了. 利用最小割最大流定理, 我们跑最大流, 再根据最后一次 BFS 得出的情报构造方案. 定理 覆盖集与独立集互补. 证明 即证明覆盖集