poj 2125 Destroying The Graph

(http://www.elijahqi.win/2017/12/28/poj-2125-destroying-the-graph/)
Description
Alice and Bob play the following game. First, Alice draws some directed graph with N vertices and M arcs. After that Bob tries to destroy it. In a move he may take any vertex of the graph and remove either all arcs incoming into this vertex, or all arcs outgoing from this vertex.
Alice assigns two costs to each vertex: Wi+ and Wi-. If Bob removes all arcs incoming into the i-th vertex he pays Wi+ dollars to Alice, and if he removes outgoing arcs he pays Wi- dollars.
Find out what minimal sum Bob needs to remove all arcs from the graph.

Input
Input file describes the graph Alice has drawn. The first line of the input file contains N and M (1 <= N <= 100, 1 <= M <= 5000). The second line contains N integer numbers specifying Wi+. The third line defines Wi- in a similar way. All costs are positive and do not exceed 106 . Each of the following M lines contains two integers describing the corresponding arc of the graph. Graph may contain loops and parallel arcs.

Output
On the first line of the output file print W — the minimal sum Bob must have to remove all arcs from the graph. On the second line print K — the number of moves Bob needs to do it. After that print K lines that describe Bob’s moves. Each line must first contain the number of the vertex and then ‘+’ or ‘-’ character, separated by one space. Character ‘+’ means that Bob removes all arcs incoming into the specified vertex and ‘-’ that Bob removes all arcs outgoing from the specified vertex.

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 +

Source
Northeastern Europe 2003, Northern Subregion
题意:给定n个点 m条边 每次有两种操作 1、删除和这个点连接的入边 2、删除和这个点连接的出边
对于每个点的两种操作分别都有一个权值现在我们要求 求出移除n个点的最小代价
那么显然可以看出 一个点 要么移除出边 要么移除入边这两个只能二选一 这就变成了一个最小点集覆盖问题 我首先建一下图 因为第一行给的是所有的出边 所以我分两列 左边的点边权是入的 右边的点边权是出的 那么 如果这两个之间原来有边的话 那么 就从左边向右边连inf的边 这样就只能在左右两个节点的边里二选一了 然后跑一下最小割 即可求出 我要的数值 至于路径如何输出 我们首先要知道 割边一定残余容量为0 但是残余容量为0的边不一定是割边 如果一个点发出的边的权值都是0那么我这个点与之前的点之间连边就是割边了 所以我设立visit数组 标记下是否访问 每次顺着残余网络容量为正的地方去dfs每次标记一下第一次访问的点 然后 最后要输出的时候看一看我源点是否访问 如果已经访问过源点 那么源点不能访问的点的出边就是被割去的 如果我没访问过汇点 而访问过那个点 说明这个点的入边一定被割去了

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 220
using namespace std;
inline char gc(){
    static char now[1<<16],*T,*S;
    if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x;
}
int num=1,h[N],level[N],T,n,m,q1[N],q2[N];bool visit[N];
struct node{
    int y,z,next;
}data[22000];
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;
}
inline bool bfs(){
    memset(level,0,sizeof(level));level[0]=1;queue<int>q;q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (level[y]||!z) continue;level[y]=level[x]+1;q.push(y);if (y==T) return 1;
        }
    }return 0;
}
inline int dfs(int x,int s){
    if (x==T) return s;int ss=s;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (level[x]+1==level[y]&&z){
            int xx=dfs(y,min(s,z));if (!xx) level[y]=0;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if (!s) return ss;
        }
    }return ss-s;
}
inline void dfs1(int x){
    visit[x]=1;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (visit[y]||!z) continue;dfs1(y);
    }
}
int main(){
    freopen("poj2125.in","r",stdin);
    n=read();m=read();T=n*2+1;
    for (int i=1;i<=n;++i) insert1(i+n,T,read());
    for (int i=1;i<=n;++i) insert1(0,i,read());
    for (int i=1;i<=m;++i){int x=read(),y=read();insert1(x,y+n,inf);}
    int ans=0;while(bfs()) ans+=dfs(0,inf);
    printf("%d\n",ans);dfs1(0);int tot1=0,tot2=0;
    for (int i=h[0];i;i=data[i].next){
        int y=data[i].y;if (visit[0]&&!visit[y]) q1[++tot1]=y;
    }
    for (int i=h[T];i;i=data[i].next){
        int y=data[i].y;if (!visit[T]&&visit[y]) q2[++tot2]=y-n;
    }printf("%d\n",tot1+tot2);
    for (int i=1;i<=tot1;++i) printf("%d -\n",q1[i]);
    for (int i=1;i<=tot2;++i) printf("%d +\n",q2[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值