您的位置:首页 > 其它

POJ 2152 Destroying The Graph【最小点权覆盖+最小割】

2012-08-11 19:05 357 查看
题意: 有一个有 N 个节点M 条边的的有向图,每个点对应两种操作:

将该点所有入边删除 ‘+’ ,对应一个花费,将该点所有出边删除 ‘ - ’亦对应一个花费

问如何操作将所有边删除使得花费的总和最小。

分析: 建图:

将每个点拆分成两个点, i(左点) 和 i+n(右点) ,

建立一个源点 s=0,汇点 u=2*n+1

在源点和每个点左面的点连一条边,容量为删除出边的费用

在汇点和每个点右面的点连一条边,容量为删除入边的费用

如果有 u 到 v 的边,在u 和 v +n 之间连一条边,容量为无穷大

求出最大流即最小割

从源点深搜找出残流不为 0 的点,把这些点标记掉,剩下的为标记的点即为删除的顶点。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define INF 0x1f1f1f1f
#define min(a,b)(a)<(b)?(a):(b)
#define clr(x)memset(x,0,sizeof(x))
#define maxn 210
struct node
{
int c,next,to;
}e[100000];
int tot;
int head[maxn];
void add(int s,int t,int flow)
{
e[tot].to=t;
e[tot].c=flow;
e[tot].next=head[s];
head[s]=tot++;
}
int max_flow(int st,int end,int n)
{
int numh[maxn],h[maxn],edge[maxn],pre[maxn];
int cur_flow,maxflow=0,u,tmp,neck,i;
clr(h);  clr(numh);
memset(pre,0xff,sizeof(pre));
for(i=0;i<=n;i++)
edge[i]=head[i];
numh[0]=n+1;
u=st;
while(h[st]<n+1)
{
if(u==end)
{
cur_flow=INF;
for(i=st;i!=end;i=e[edge[i]].to)
if(cur_flow>e[edge[i]].c)
{
neck=i;
cur_flow=e[edge[i]].c;
}
for(i=st;i!=end;i=e[edge[i]].to)
{
tmp=edge[i];
e[tmp].c-=cur_flow;
e[tmp^1].c+=cur_flow;
}
maxflow+=cur_flow;
u=neck;
}
for(i=edge[u];i!=-1;i=e[i].next)
if(e[i].c&&h[u]==h[e[i].to]+1)
break;
if(i!=-1)
{
edge[u]=i;
pre[e[i].to]=u;
u=e[i].to;
}
else
{
if(--numh[h[u]]==0)
break;
edge[u]=head[u];
for(tmp=n,i=head[u];i!=-1;i=e[i].next)
if(e[i].c)
tmp=min(tmp,h[e[i].to]);
h[u]=tmp+1;
++numh[h[u]];
if(u!=st)
u=pre[u];
}
}
return maxflow;
}
int v[maxn];
void dfs(int r)
{
v[r]=1;
int i,k;
for(i=head[r];i!=-1;i=e[i].next)
{
k=e[i].to;
if(!v[k]&&e[i].c)
{
v[k]=1;
dfs(k);
}
}
}
int main()
{
int i,n,m,s,u,p,a,b,res,num;
while(scanf("%d%d",&n,&m)!=EOF)
{
s=0;
u=2*n+1;
memset(head,0xff,sizeof(head));
tot=0;
for(i=1;i<=n;i++)
{
scanf("%d",&p);
add(i+n,u,p);
add(u,i+n,0);
}
for(i=1;i<=n;i++)
{
scanf("%d",&p);
add(s,i,p);
add(i,s,0);
}
while(m--)
{
scanf("%d%d",&a,&b);
add(a,b+n,INF);
add(b+n,a,0);
}
res=max_flow(s,u,n*2+1);
num=0;
clr(v);
dfs(s);
for(i=1;i<=n;i++)
{
if(!v[i])
num++;
if(v[i+n])
num++;
}
printf("%d\n%d\n",res,num);
for(i=1;i<=n;i++)
{
if(!v[i])
printf("%d -\n",i);
if(v[i+n])
printf("%d +\n",i);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: