poj 2516 最小费用最大流

题意:给定n个采购商所需要的k种货物,m个供应商提供的k种货物,对每种货物,从供应商运到采购商所需要的单位路费,求最小总费用。
解法:构图:采购商与源点构边,容量为对应货物该采购商的需求量,费用为0。
            采购商与供应商构边,容量为对应货物该采购商的需求量,费用为对应货物从供应商运到采购商所需要的单位路费。
            供应商与汇点构边,容量为供应商对该货物的供应量,费用为0。
      对每种货物求最小费用最大流,最后相加即可。
      若每种货物供应量小于需求量,则输出-1。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<climits>
using namespace std;
const int MAX=55;
int need[MAX][MAX],provide[MAX][MAX],cap[MAX<<1][MAX<<1],cost[MAX<<1][MAX<<1],a[MAX],b[MAX];
bool inque[MAX<<1];
int que[MAX*MAX],f,r;
int dis[MAX<<1],pre[MAX<<1];
/*
求最小费用路,并更新权值。
*/
int spfa(int st,int ed)
{
    memset(inque,false,sizeof(inque));
    inque[st]=true;
    f=0;
    r=1;
    que[f]=st;
    for(int i=0; i<=ed; i++)
    {
        dis[i]=INT_MAX;
        pre[i]=-1;
    }
    dis[st]=0;
    while(f<r)
    {
        int fro=que[f++];
        inque[fro]=false;
        for(int i=1; i<=ed; i++)
            if(cap[fro][i]>0&&dis[fro]+cost[fro][i]<dis[i])
            {
                dis[i]=dis[fro]+cost[fro][i];
                pre[i]=fro;
                if(!inque[i])
                {
                    que[r++]=i;
                    inque[i]=true;
                }
            }
    }
    if(dis[ed]==INT_MAX)
        return -1;
    int minflow=INT_MAX;
    for(int i=ed; pre[i]!=-1; i=pre[i])
        if(cap[pre[i]][i]<minflow)
            minflow=cap[pre[i]][i];
    for(int i=ed; pre[i]!=-1; i=pre[i])
    {
        cap[pre[i]][i]-=minflow;
        cap[i][pre[i]]+=minflow;
    }
    return dis[ed]*minflow;
}
/*
除了采购商与源点构边,采购商与供应商构边,供应商与汇点构边外,还需注意其他间接点默认相连的边容量清零!!!
*/
int main()
{
    int n,m,k,i,j,mincost;
    bool flag;
    //freopen("in.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        if(!n&&!m&&!k)
            break;
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        flag=true;
        for(i=1; i<=n; i++)
            for(j=1; j<=k; j++)
            {
                scanf("%d",&need[i][j]);
                a[j]+=need[i][j];
            }
        for(i=1; i<=m; i++)
            for(j=1; j<=k; j++)
            {
                scanf("%d",&provide[i][j]);
                b[j]+=provide[i][j];
            }
        //判断是否供过于求
        for(i=1; i<=k&&flag; i++)
            if(a[i]>b[i])
            {
                flag=false;
            }
        int kind=k;
        mincost=0;
        while(k--)
        {
            memset(cap,0,sizeof(cap));
            //采购商与供应商构边
            for(i=1; i<=n; i++)
                for(j=n+1; j<=n+m; j++)
                {
                    scanf("%d",&cost[i][j]);
                    if(!flag)
                        continue;
                    cost[j][i]=-cost[i][j];
                    cap[i][j]=need[i][kind-k];
                }
            if(!flag)
                continue;
            int st=0,ed=n+m+1;
            //采购商与源点构边
            for(i=1; i<=n; i++)
            {
                cost[st][i]=cost[i][st]=0;
                cap[st][i]=need[i][kind-k];
            }
            //供应商与汇点构边
            for(i=n+1; i<=n+m; i++)
            {
                cost[ed][i]=cost[i][ed]=0;
                cap[i][ed]=provide[i-n][kind-k];
            }
            int tmp;
            while((tmp=spfa(st,ed))!=-1)
                mincost+=tmp;
        }
        if(!flag)
            mincost=-1;
        printf("%d\n",mincost);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值