POJ 2125 Destroying The Graph 最小点权覆盖集

来源:互联网 发布:高淇java什么方向 编辑:程序博客网 时间:2024/06/06 05:58
点击打开链接
Destroying The Graph
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 6840 Accepted: 2162 Special Judge

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 61 2 34 2 11 21 13 21 23 12 3

Sample Output

531 +2 -2 +

Source

Northeastern Europe 2003, Northern Subregion

给你一个有向图,每个点有两种操作,一种是删除它的所有入边,一种是删除它的所有出边,并且每个删除都有一个花费,让你求最小花费使得这个图删除所有的边。
先看一下下面几个定义:
点覆盖集:是无向图的一个点集,使得该图中所有边都至少有一个端点在该集合内。
最小点覆盖集:在无向图中,点数最少的点覆盖集。
最小点权覆盖集:是在带点权无向图中,点权之和最小的点覆盖集。
此题无疑求得就是最小点权覆盖集。
二分图的最小点权覆盖算法:建立一个超级源点s,向X部每个点连边,容量为X部每个点的点权;建立一个汇点t,从Y部每个点向汇点t连边,容量为Y部每个点的点权,把二分图中的边看成是有向的,u到v连接,容量为inf。则任意一条从s到t的路径,一定具有s->u->v->t。的形式。
此题就可以转换成求二分图的最小点权覆盖,将每个点拆点,拆成a和a',那么源点与a相连,容量为W-,a‘ 与汇点相连,容量为W+,如果u,v之间有连边,那么u和 v' 相连,容量为inf,跑一遍最大流,就是最小点权覆盖。
然后输出:
从s开始搜索,1到n不能搜索到点就是选择删掉出边的点,n+1到2*n能搜到的点就是就是选择删除掉入边的点。
//572K125MS#include<stdio.h>#include<string.h>#include<algorithm>#define inf 9999999#define M 5007#define MIN(a,b) a>b?b:a;using namespace std;struct E{    int v,w,next;}edg[500000];int dis[M],gap[M],head[M],nodes;int sourse,sink,nn;int vis[M];void addedge(int u,int v,int w){    edg[nodes].v=v;    edg[nodes].w=w;    edg[nodes].next=head[u];    head[u]=nodes++;    edg[nodes].v=u;    edg[nodes].w=0;    edg[nodes].next=head[v];    head[v]=nodes++;}int dfs(int src,int aug){    if(src==sink)return aug;    int left=aug,mindis=nn;    for(int j=head[src];j!=-1;j=edg[j].next)    {    int v=edg[j].v;    if(edg[j].w)        {           if(dis[v]+1==dis[src])           {               int minn=MIN(left,edg[j].w);               minn=dfs(v,minn);               edg[j].w-=minn;               edg[j^1].w+=minn;               left-=minn;               if(dis[sourse]>=nn)return aug-left;               if(left==0)break;           }           if(dis[v]<mindis)           mindis=dis[v];        }    }        if(left==aug)        {            if(!(--gap[dis[src]]))dis[sourse]=nn;            dis[src]=mindis+1;            gap[dis[src]]++;        }        return aug-left;}int sap(int s,int e){    int ans=0;nn=e+1;    memset(dis,0,sizeof(dis));    memset(gap,0,sizeof(gap));    gap[0]=nn;    sourse=s;    sink=e;    while(dis[sourse]<nn)    ans+=dfs(sourse,inf);    return ans;}int solve(int u){    vis[u]=1;    for(int i=head[u];i!=-1;i=edg[i].next)    {        int v=edg[i].v;        if(edg[i].w&&!vis[v])            solve(v);    }}int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        int  s=0,t=2*n+1,a,u,v,count=0;        memset(head,-1,sizeof(head));        memset(vis,0,sizeof(vis));        nodes=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&a);            addedge(i+n,t,a);        }        for(int i=1;i<=n;i++)        {            scanf("%d",&a);            addedge(s,i,a);        }        while(m--)        {            scanf("%d%d",&u,&v);            addedge(u,v+n,inf);        }        printf("%d\n",sap(s,t));        solve(s);        for(int i=1;i<=n;i++)        {            if(!vis[i])count++;            if(vis[i+n])count++;        }        printf("%d\n",count);        for(int i=1;i<=n;i++)        {             if(!vis[i])printf("%d -\n",i);            if(vis[i+n])printf("%d +\n",i);        }    }    return 0;}


0 0
原创粉丝点击