UVA_10806

    这个题目我们可以把边的容量设为1,费用设为权值,然后再引入一条边N-N+1,容量设为2,费用设为0,然后去求1N+1的最小费用最大流,如果到N+1的流量为2,则输出最小费用,否则就是无解。

在老大的指点下,自己亲手把以前没有写过的费用流的邻接表形式写了一遍。

费用流应用邻接表时需要注意三个问题:

①要把一条边的正向边和反向边各建一次,如果是无向图,可以把一个无向边看成两个有向边,然后建四条边存储。

②在建边时多开一个数组op[],用于存储此边的反向边的标号,便于增广。

③在记录父节点时,同时多开一个数组edge[],用于记录父节点连向该节点的有向边的标号,用于增广。

有了邻接表形式的费用流,就可以求解存在平行边和反向边的费用流问题了。

#include<stdio.h>
#include<string.h>
int first[110],next[40010],v[40010],op[40010];
int flow[40010],cost[40010],cap[40010];
int q[110],inq[110],p[110],edge[110],d[110],N,M,E;
int init()
{
int i,a,b,w,e;
scanf("%d",&N);
if(!N)
return 0;
scanf("%d",&M);
e=0;
memset(first,-1,sizeof(first));
for(i=0;i<M;i++)
{
scanf("%d%d%d",&a,&b,&w);
v[e]=b;
cap[e]=1;
cost[e]=w;
next[e]=first[a];
first[a]=e;
op[e]=e+1;
e++;
v[e]=a;
cap[e]=0;
cost[e]=-w;
next[e]=first[b];
first[b]=e;
op[e]=e-1;
e++;
v[e]=a;
cap[e]=1;
cost[e]=w;
next[e]=first[b];
first[b]=e;
op[e]=e+1;
e++;
v[e]=b;
cap[e]=0;
cost[e]=-w;
next[e]=first[a];
first[a]=e;
op[e]=e-1;
e++;
}
v[e]=N+1;
cap[e]=2;
cost[e]=0;
next[e]=first[N];
first[N]=e;
op[e]=e+1;
e++;
v[e]=N;
cap[e]=0;
cost[e]=0;
next[e]=first[N+1];
first[N+1]=e;
op[e]=e-1;
e++;
E=e;
return 1;
}
int check()
{
int i,j,k,e,a,f,c,u,rear,front;
memset(flow,0,sizeof(flow));
f=c=0;
while(1)
{
front=rear=0;
memset(inq,0,sizeof(inq));
for(i=1;i<=N+1;i++)
d[i]=1000000001;
d[1]=0;
q[rear++]=1;
inq[1]=1;
while(front!=rear)
{
u=q[front++];
inq[u]=0;
if(front>N+1)
front=0;
for(e=first[u];e!=-1;e=next[e])
if(d[u]+cost[e]<d[v[e]]&&cap[e]>flow[e])
{
d[v[e]]=d[u]+cost[e];
p[v[e]]=u;
edge[v[e]]=e;
if(!inq[v[e]])
{
q[rear++]=v[e];
if(rear>N+1)
rear=0;
inq[v[e]]=1;
}
}
}
if(d[N+1]>1000000000)
break;
a=1000000001;
for(u=N+1;u!=1;u=p[u])
{
e=edge[u];
if(cap[e]-flow[e]<a)
a=cap[e]-flow[e];
}
for(u=N+1;u!=1;u=p[u])
{
e=edge[u];
flow[e]+=a;
flow[op[e]]-=a;
}
c+=d[N+1]*a;
f+=a;
}
if(f==2)
return c;
else
return -1;
}
int main()
{
int k;
while(init())
{
k=check();
if(k<0)
printf("Back to jail\n");
else
printf("%d\n",k);
}
return 0;
}


posted on 2011-09-30 23:28  Staginner  阅读(428)  评论(0编辑  收藏  举报