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

本文详细介绍了如何使用最大流算法求解最小点权覆盖集问题,通过将原始问题转化为二分图最小点权覆盖问题,利用增广路径法找到最优解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Destroying The Graph
Time Limit: 2000MS Memory Limit: 65536K
Total 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 10 6 . 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

给你一个有向图,每个点有两种操作,一种是删除它的所有入边,一种是删除它的所有出边,并且每个删除都有一个花费,让你求最小花费使得这个图删除所有的边。
先看一下下面几个定义:
点覆盖集:是无向图的一个点集,使得该图中所有边都至少有一个端点在该集合内。
最小点覆盖集:在无向图中,点数最少的点覆盖集。
最小点权覆盖集:是在带点权无向图中,点权之和最小的点覆盖集。
此题无疑求得就是最小点权覆盖集。
二分图的最小点权覆盖算法:建立一个超级源点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能搜到的点就是就是选择删除掉入边的点。
//572K	125MS
#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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值