POJ2516 Minimum Cost 最大流最小费用典型题目

刚接触spfa算法。

我的理解是spfa算法实质是bellman算法的改进版,可以处理负值,这是和dij的本质区别,也是这里不能用dij的原因。

spfa算法的队列的大小一定要注意!开小了悲情无限。

而最大流最小费用的思路说简单了就是最大流的实现时,以前没有最小费用这个约束的时候,我们很随意的可以直接用bfs来搜索增广路径来解最大流。

但此时有最小费用这个约束。我们用最短路来搜索增广路径。

这样来求得的最大流则满足题意。

#include<iostream>

using namespace std;
int n,m,k;
const int inf=9999999;
const int N=111;
int shop[N][N];
int storage[N][N];
int mat[N][N];
int cost[N][N];
int ans;
bool visit[N];
int que[N*N],pre[N],dis[N];//que的大小设定不是简单的N,之前在广搜的时候习惯是N,上次一题就是直接写N,就一直TLE,今天才意识到,泪流满面。
bool spfa()
{
int head=1,tail=1;
for(int i=0;i<=n+m+1;i++)
{
dis[i]=inf;
visit[i]=false;
}
dis[0]=0;
que[tail++]=0;
visit[0]=true;
while(head<tail)
{
int now=que[head++];
for(int i=0;i<=n+m+1;i++)
if(mat[now][i]>0&&dis[i]>dis[now]+cost[now][i])
{
dis[i]=dis[now]+cost[now][i];
pre[i]=now;
if(!visit[i])
{
visit[i]=true;
que[tail++]=i;
//if
}
}
visit[now]=false;

}
if(dis[n+m+1]==inf)
return false;
return true;
}
int main()
{
while(scanf("%d%d%d",&n,&m,&k),n!=0||m!=0||k!=0)
{
ans=0;
int need[N];
memset(need,0,sizeof(need));
memset(shop,0,sizeof(shop));
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
{
scanf("%d",&shop[i][j]);
need[j]+=shop[i][j];
}
int supply[N];
memset(supply,0,sizeof(supply));
memset(storage,0,sizeof(storage));
for(int i=1;i<=m;i++)
for(int j=1;j<=k;j++)
{
scanf("%d",&storage[i][j]);
supply[j]+=storage[i][j];
}
bool flag=false;
for(int i=1;i<=k;i++)
{
if(supply[i]<need[i])
{
flag=true;
break;
}
}
for(int t=1;t<=k;t++)
{
memset(mat,0,sizeof(mat));
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%d",&cost[j][m+i]);
cost[m+i][j]=-cost[j][m+i];//这就是用spfa而不能用dij的原因
mat[j][m+i]=inf;
}
if(flag)
continue;
for(int i=1;i<=m;i++)
{
mat[0][i]=storage[i][t];
cost[0][i]=cost[i][0]=0;
}
for(int i=1;i<=n;i++)
{
mat[m+i][n+m+1]=shop[i][t];
cost[m+i][n+m+1]=cost[n+m+1][m+i]=0;


}
while(spfa())
{
int min=inf;
for(int i=m+n+1;i!=0;i=pre[i])
{
min=(min<mat[pre[i]][i]?min:mat[pre[i]][i]);
}
for(int i=n+m+1;i!=0;i=pre[i])
{
mat[pre[i]][i]-=min;
mat[i][pre[i]]+=min;
ans+=cost[pre[i]][i]*min;
}
}
}
if(flag)
printf("-1\n");
else
printf("%d\n",ans);
}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值