Destroying The Graph (poj 2125 最小点权覆盖)

来源:互联网 发布:招在家工作淘宝客服 编辑:程序博客网 时间:2024/05/17 02:18

Language:
Destroying The Graph
Time Limit: 2000MS Memory Limit: 65536KTotal Submissions: 7676 Accepted: 2470 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


题意:有一张有向图n个点m有向边,现在每次删除一个点的所有入边费用为W+,或者删除一个点的所有的出边费用为W-,问删除所有的边最小费用是多少。

思路:最小点权覆盖问题,具体贴上《最小割模型在信息学竞赛中的应用》中的阐述:

回顾与此模型相关的模型。简化权的条件后,可以借鉴的是二分图匹配的大流解法。 它加入了额外的源s和汇t,将匹配以一条条s->u->v->t形式的流路径“串联”起来。匹配的 限制是在点上,恰当地利用了流的容量限制。而点覆盖集的限制在边上,最小割是最大流的对偶问题,对偶往往是将问题的性质从点转边,从边转点。可以尝试着转化到最小割模型。 基于以上尝试动机,建立一个源s,向X 部每个点连边;建立一个汇t,从Y部每个点向汇t连边,把二分图中的边看成是有向的。则任意一条从s到t的路径,一定具有s->u->v->t的形式。割的性质是不存在一条从s到t的路径。故路径上的三条边(s,u),(u,v),(v,t)中至少有一条边在割中。若人为地令(u,v)不可能在最小割中,即令其容量为正无限c(u,v)=INF。则条件简化为(s,u),(v,t)中至少一条边在小割中,正好与点覆盖集限制条件的形式相符( u∈V`,v∈V`至少一个成立)。而目标是最小化点权之和,恰好也是最小割的优化目标。 形式化以上推理过程,将原问题的图G转化为网络N=(VN,EN)的最小割模型。 在原图点集的基础上增加源s和汇t:将每条二分图的边(u,v)∈E替换为容量为c(u,v)=∞的有向边 ,(u,v)∈ EN;增加源s到X部的点u的有向边(s,u) ∈ EN,容量即该点权值c(u,v)=Wu;增加Y部的点v到汇t的有向边(v,t)∈EN ,同样,容量为该点权值c(u,v)=Wv。


代码:

#include <iostream>#include <functional>#include <cstdio>#include <cstring>#include <algorithm>#include <cmath>#include <string>#include <map>#include <stack>#include <vector>#include <set>#include <queue>#pragma comment (linker,"/STACK:102400000,102400000")#define pi acos(-1.0)#define eps 1e-6#define lson rt<<1,l,mid#define rson rt<<1|1,mid+1,r#define FRE(i,a,b)  for(i = a; i <= b; i++)#define FREE(i,a,b) for(i = a; i >= b; i--)#define FRL(i,a,b)  for(i = a; i < b; i++)#define FRLL(i,a,b) for(i = a; i > b; i--)#define mem(t, v)   memset ((t) , v, sizeof(t))#define sf(n)       scanf("%d", &n)#define sff(a,b)    scanf("%d %d", &a, &b)#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)#define pf          printf#define DBG         pf("Hi\n")typedef long long ll;using namespace std;#define INF 0x3f3f3f3f#define mod 1000000009const int maxn = 105;const int MAXN = 205;const int MAXM = 200010;struct Edge{    int to,next,cap,flow;}edge[MAXM];int n,m;int a[maxn],b[maxn];int tol;int head[MAXN];int gap[MAXN],dep[MAXN],pre[MAXN],cur[MAXN];void init(){    tol=0;    memset(head,-1,sizeof(head));}//加边,单向图三个参数,双向图四个参数void addedge(int u,int v,int w,int rw=0){    edge[tol].to=v; edge[tol].cap=w; edge[tol].next=head[u];    edge[tol].flow=0; head[u]=tol++;    edge[tol].to=u; edge[tol].cap=rw; edge[tol].next=head[v];    edge[tol].flow=0; head[v]=tol++;}//输入参数:起点,终点,点的总数//点的编号没有影响,只要输入点的总数int sap(int start,int end,int N){    memset(gap,0,sizeof(gap));    memset(dep,0,sizeof(dep));    memcpy(cur,head,sizeof(head));    int u=start;    pre[u]=-1;    gap[0]=N;    int ans=0;    while (dep[start]<N)    {        if (u==end)        {            int Min=INF;            for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])                if (Min>edge[i].cap-edge[i].flow)                    Min=edge[i].cap-edge[i].flow;            for (int i=pre[u];i!=-1;i=pre[edge[i^1].to])            {                edge[i].flow+=Min;                edge[i^1].flow-=Min;            }            u=start;            ans+=Min;            continue;        }        bool flag=false;        int v;        for (int i=cur[u];i!=-1;i=edge[i].next)        {            v=edge[i].to;            if (edge[i].cap-edge[i].flow && dep[v]+1==dep[u])            {                flag=true;                cur[u]=pre[v]=i;                break;            }        }        if (flag)        {            u=v;            continue;        }        int Min=N;        for (int i=head[u];i!=-1;i=edge[i].next)            if (edge[i].cap-edge[i].flow && dep[edge[i].to]<Min)            {                Min=dep[edge[i].to];                cur[u]=i;            }        gap[dep[u]]--;        if (!gap[dep[u]]) return ans;        dep[u]=Min+1;        gap[dep[u]]++;        if (u!=start) u=edge[pre[u]^1].to;    }    return ans;}bool vis[MAXN];void dfs(int u){    vis[u]=true;    for (int i=head[u];~i;i=edge[i].next)    {        int v=edge[i].to;        if (edge[i].cap-edge[i].flow>0&&!vis[v])        {            dfs(v);        }    }}int main(){#ifndef ONLINE_JUDGE    freopen("C:/Users/lyf/Desktop/IN.txt","r",stdin);#endif    int i,j,u,v;    while (~scanf("%d%d",&n,&m))    {        for (i=1;i<=n;i++)            scanf("%d",&a[i]);        for (i=1;i<=n;i++)            scanf("%d",&b[i]);        init();        for (i=0;i<m;i++)        {            scanf("%d%d",&u,&v);            addedge(u,v+n,INF);        }        for (i=1;i<=n;i++)        {            addedge(0,i,b[i]);            addedge(i+n,2*n+1,a[i]);        }        int ans=sap(0,2*n+1,2*n+2);        printf("%d\n",ans);        dfs(0);        int num=0;        vector<int>aa,bb;        for (i=1;i<=n;i++)        {            if (!vis[i])            {                aa.push_back(i);                num++;            }        }        for (i=n+1;i<=2*n;i++)        {            if (vis[i])            {                bb.push_back(i-n);                num++;            }        }        printf("%d\n",num);        for (i=0;i<aa.size();i++)        {            printf("%d -\n",aa[i]);        }        for (i=0;i<bb.size();i++)        {            printf("%d +\n",bb[i]);        }    }    return 0;}



0 0
原创粉丝点击