您的位置:首页 > 其它

poj 2516 Minimum Cost (最小费用最大流)

2014-01-12 16:14 417 查看
题目链接: poj 2516

题目大意: 给出N间商店和它们对K种商品的需求,再给出M个供应商和K种商品的库存

然后再给出第x种商品从j供应商运输到i商店的单位运输费用

求N间商店对商品的需求能否得到满足,并且输出最小费用

解题思路: 如果直接建图会超时,因为顶点数太多

所以根据每种商品,进行K次最小费用流:

1.建立超级源点,源点指向N间商店,容量为第i间商店对第i1种商品的需求,费用为0

2.建立超级汇点,M个供应商指向汇点,容量为第j个供应商的第i1种商品的库存,费用为0

3.建立N*M条边,每条边从第i间商店指向第j个供应商,容量为无穷大(20000),费用为单位运输费用

PS: 最小费用最大流与一般增广路的区别在于,每次寻找的增广路都是代价最小的路径

代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 110
#define INF 0x3f3f3f3f
#define Min(a,b) ((a<b)?a:b)
int S,E,Index,dist[MAX],pre[MAX],visit[MAX],listb[2000010];
int path[MAX],In1[53][53],In2[53][53],In3[53][53][53];
int Map[MAX][MAX],Value[MAX][MAX];

struct snode{
int c,w,to,next;
}Edge[MAX*MAX*10];

void Add_edge(int a,int b,int c,int d)
{
Value[a][b]=d;     //标记改变的费用
if(c>=0)            //构建残留网络
Map[a][b]=c;
Edge[Index].to=b,Edge[Index].c=c;
Edge[Index].w=d,Edge[Index].next=pre[a];
pre[a]=Index++;
}

bool BFS()
{
int i,e,s,v,vv;
s=e=0;
memset(path,-1,sizeof(path));
memset(dist,INF,sizeof(dist));
memset(visit,0,sizeof(visit));
listb[s++]=S;visit[S]=1; dist[S]=0;
while(s!=e)
{
v=listb[e++];
visit[v]=0;
for(i=pre[v];i!=-1;i=Edge[i].next)
{
vv=Edge[i].to;
if(Map[v][vv]>0&&(dist[vv]==INF||dist[vv]>dist[v]+Edge[i].w))
{     //最短路寻找代价最小的增广路
path[vv]=v;
dist[vv]=dist[v]+Edge[i].w;
if(!visit[vv])      //防止重复入队列
{
visit[vv]=1;
listb[s++]=vv;
}
}
}
}
if(dist[E]==INF)  //不能到达汇点,增广结束
return false;
return true;
}

int Find()
{
int sum=0,i,MIN=INF;
for(i=E;i!=-1;i=path[i])     //找短板
{
if(path[i]!=-1)
MIN=Min(MIN,Map[path[i]][i]);
}
for(i=E;i!=-1;i=path[i])     //更新增广路
{
if(path[i]!=-1)
{
Map[path[i]][i]-=MIN;
Map[i][path[i]]+=MIN;
sum=sum+Value[path[i]][i]*MIN;  //代价是每条边的权值,所以不需要乘与
}
}
return sum;
}

int Solve()
{
int sum=0;
while(BFS())
sum+=Find();
return sum;
}

int main()
{
int i,n,j,m,k,aa[52],bb[52],pd;
while(scanf("%d%d%d",&n,&m,&k)!=EOF)
{
Index=pd=0;
if(n==0&&m==0&&k==0)
break;
memset(pre,-1,sizeof(pre));
memset(Map,0,sizeof(Map));
memset(Value,0,sizeof(Value));
memset(aa,0,sizeof(aa));
memset(bb,0,sizeof(bb));
int a,b,c,d;
S=0,E=n*k+m*k+1;
for(i=1;i<=n;i++)
{
for(j=1;j<=k;j++)
{
scanf("%d",&In1[i][j]);
aa[j]+=In1[i][j];
}
}
for(i=1;i<=m;i++)
{
for(j=1;j<=k;j++)
{
scanf("%d",&In2[i][j]);
bb[j]+=In2[i][j];
}
}
for(int i1=1;i1<=k;i1++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=m;j++)
scanf("%d",&In3[i1][i][j]);
}
}
for(i=1;i<=k;i++)
{
if(bb[i]<aa[i])   //若不满足则直接输出-1
{
pd=1;
break;
}
}
if(pd==1)
{
printf("-1\n");
continue;
}
int temp=0;
S=0,E=n+m+1;
for(i=1;i<=k;i++)
{
Index=0;
memset(pre,-1,sizeof(pre));
memset(Map,0,sizeof(Map));
memset(Value,0,sizeof(Value));
for(j=1;j<=n;j++)   //左边
{
a=S,b=j,c=In1[j][i],d=0;
Add_edge(a,b,c,d);
Add_edge(b,a,-c,-d);
}
for(j=1;j<=n;j++)   //中间
{
for(int j1=1;j1<=m;j1++)
{
a=j,b=n+j1;
c=20000;
d=In3[i][j][j1];
Add_edge(a,b,c,d);
Add_edge(b,a,-c,-d);
}
}
for(j=1;j<=m;j++)   //后面
{
a=n+j,b=E,c=In2[j][i],d=0;
Add_edge(a,b,c,d);
Add_edge(b,a,-c,-d);
}
temp+=Solve();
}
printf("%d\n",temp);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: