Destroying The Graph
Time Limit: 2000MS | Memory Limit: 65536K | |||
Total Submissions: 5865 | Accepted: 1864 | 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.
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
题意:给你一张图,有N个点和M条边,然后你要在这N个点中选取几个点移除,没取一个点,就删去与之有关的弧。如果如果删去该点的所有出度,要花去W-的费用,删去所有入度就要花去W+的费用。问花费最小的费用取点删去所有的边。
思路:最小点权覆盖,可以当做是最小割,挺好好建图的,就是最后遍历求如何取点想了挺久......
我们首先设源点s和汇点t,然后把每个点拆分成两个点。对于a->b有连接的边,我们连接一条(a,n+b,oo)的边,oo为无穷大,n+b为b点拆分的点。然后对于所有点从s到i连接一条权值为W-的边,从i+n到t连接一条权值为W+的边。
其实是二分匹配。这里求出的最大流就是最小割,最小割就是最小点权覆盖。
对于最后的取点,因为求的是最小割,那么从s开始遍历,对于i<=n不能遍历到的点,也就是被取走了,输出i -,对于能遍历到的点,并且i>n,说明与之相连的j->i,之前的s->j的边不是最小割,j不取,i是最小割中的点,所以输出i +。
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int mm=100000;
const int mn=300;
const int oo=1000000000;
int node,s,t,edge;
int to[mm],flow[mm],next[mm];
int head[mn],work[mn],dis[mn],q[mn];
bool vis[mn];
inline int min(int a,int b)
{
return a<b?a:b;
}
inline void init(int nn,int ss,int tt)
{
node=nn,s=ss,t=tt,edge=0;
for(int i=0; i<node; ++i) head[i]=-1;
}
inline void add(int u,int v,int c)
{
to[edge]=v,flow[edge]=c,next[edge]=head[u],head[u]=edge++;
to[edge]=u,flow[edge]=0,next[edge]=head[v],head[v]=edge++;
}
bool bfs()
{
int i,u,v,l,r=0;
for(i=0; i<node; ++i) dis[i]=-1;
dis[q[r++]=s]=0;
for(l=0; l<r; ++l)
for(i=head[u=q[l]]; i>=0; i=next[i])
if(flow[i]&&dis[v=to[i]]<0)
{
dis[q[r++]=v]=dis[u]+1;
if(v==t)return 1;
}
return 0;
}
int dfs(int u,int maxf)
{
if(u==t) return maxf;
for(int &i=work[u],v,tmp; i>=0; i=next[i])
if(flow[i]&&dis[v=to[i]]==dis[u]+1&&(tmp=dfs(v,min(maxf,flow[i])))>0)
{
flow[i]-=tmp;
flow[i^1]+=tmp;
return tmp;
}
return 0;
}
int dinic()
{
int i,ret=0,delta;
while(bfs())
{
for(i=0; i<node; ++i) work[i]=head[i];
while(delta=dfs(s,oo))ret+=delta;
}
return ret;
}
void flag(int now)
{
vis[now]=1;
for(int i=head[now]; ~i; i=next[i])
{
if(flow[i]&&!vis[to[i]])
flag(to[i]);
}
}
int main()
{
int n,m,u,v,i,cost,cnt;
while(cin>>n>>m)
{
init(2*n+2,0,2*n+1);
memset(vis,0,sizeof(vis));
cnt=0;
for(i=n+1; i<=2*n; i++)
{
scanf("%d",&cost);
add(i,t,cost);
}
for(i=1; i<=n; i++)
{
scanf("%d",&cost);
add(s,i,cost);
}
while(m--)
{
scanf("%d%d",&u,&v);
add(u,v+n,oo);
}
int ans=dinic();
printf("%d\n",ans);
flag(s);
for(i=1; i<=2*n; i++)
{
if(vis[i]&&i>n) cnt++;
else if(!vis[i]&&i<=n) cnt++;
}
printf("%d\n",cnt);
for(i=1; i<=2*n; i++)
{
if(vis[i]&&i>n) printf("%d +\n",i-n);
else if(!vis[i]&&i<=n) printf("%d -\n",i);
}
}
return 0;
}