给你一个有向图,每个点有两种操作,一种是删除它的所有入边,一种是删除它的所有出边,并且每个删除都有一个花费,w- 是出边的花费 ,w+ 是入边的花费 ; 让你求最小花费使得这个图删除所有的边。
先看一下下面几个定义:
点覆盖集:是无向图的一个点集,使得该图中所有边都至少有一个端点在该集合内。
最小点覆盖集:在无向图中,点数最少的点覆盖集。
最小点权覆盖集:是在带点权无向图中,点权之和最小的点覆盖集。
此题无疑求得就是最小点权覆盖集。
二分图的最小点权覆盖算法:建立一个超级源点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->->t的路径,所以是s和u的容量为w- ,w-代表出边 ,w+代表入边 ,所以 v`到t容量为w+)
然后输出:
从s开始搜索,我们知道最小割边集 都是那些跑完最大流后 边的容量为0的边 ; 要么割的是s到u的边 , 此时1到n不能搜索到点就是选择删掉出边的点,要么割的是u`到t的边,此时n+1到2*n能搜到的点就是就是选择删除掉入边的点。
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 300 ;
const int M=50000 ;
const int inf=1<<30 ;
struct node
{
int u,v,c,next ;
}edge[M] ;
int head[N],pre[N],cur[N],gap[N],dis[N],vist[N] ;
int top ;
void add(int u ,int v,int c)
{
edge[top].u=u;
edge[top].v=v;
edge[top].c=c;
edge[top].next=head[u];
head[u]=top++;
edge[top].u=v;
edge[top].v=u;
edge[top].c=0;
edge[top].next=head[v];
head[v]=top++;
}
int sap(int s,int t ,int nv)
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
int u,v,minflow=inf,flow=0;
for(int i = 0 ; i <nv ; i++) cur[i]=head[i] ;
u=pre[s]=s;
gap[s]=nv;
while(dis[s] < nv )
{
loop :
for(int &j=cur[u] ; j!=-1 ; j=edge[j].next)
{
v=edge[j].v ;
if(edge[j].c > 0 && dis[u] == dis[v] +1 )
{
minflow = min(minflow ,edge[j].c) ;
pre[v]=u;
u=v ;
if(v==t)
{
for( u =pre[v] ; v!=s ;v=u,u=pre[u])
{
edge[cur[u]].c -= minflow ;
edge[cur[u]^1].c += minflow ;
}
flow += minflow ;
minflow = inf ;
}
goto loop ;
}
}
int mindis=nv ;
for(int i = head[u] ; i!=-1 ; i=edge[i].next)
{
v=edge[i].v ;
if(edge[i].c > 0 && dis[v] < mindis)
{
mindis = dis[v] ;
cur[u]=i ;
}
}
if(--gap[dis[u]]==0) break ;
gap[ dis[u] = mindis +1 ]++ ;
u=pre[u] ;
}
return flow ;
}
void dfs(int u)
{
vist[u] = 1;
for(int i = head[u] ; i!=-1 ; i=edge[i].next)
{
int v = edge[i].v ;
if(!vist[v] && edge[i].c)
dfs(v) ;
}
}
int main()
{
int n,m ,x,u,v;
while(~scanf("%d%d",&n,&m))
{
top=0 ;
memset(head,-1,sizeof(head)) ;
int s=0,t=2*n+1 ;
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&x) ;
add(i+n,t,x) ; //入边
}
for(int i = 1 ; i <= n ; i++)
{
scanf("%d",&x) ;
add(s,i,x) ;//出边
}
for(int i = 1 ; i <= m ; i++)
{
scanf("%d%d",&u,&v) ;
add(u,v+n,inf) ;
}
int ans = sap(s,t,t+1) ;
printf("%d\n",ans) ;
memset(vist,0,sizeof(vist)) ;
dfs(s) ;
int sum = 0;
for(int i = 1 ; i <= n ; i++)
{
if(!vist[i]) sum++; //割边s->i
if(vist[i+n]) sum++; //割边i+n->t ;
}
printf("%d\n",sum) ; //割边数量
for(int i = 1 ; i <= n ;i++)
{
if(!vist[i])
printf("%d -\n" ,i ) ;
if(vist[i+n])
printf("%d +\n", i) ;
}
}
return 0;
}