最小费用最大流+(对最小费用最大流的理解)

网络流 专栏收录该内容
10 篇文章 0 订阅

题目链接:https://cn.vjudge.net/contest/68128#problem/E

具体思路:图的建立方式, 超级源点 - >  供应商 - > 顾客 - > 超级汇点。

因为有多种商品,所以可以简化构图过程,每一次求一种商品的最小费用最大流,然后最终将所有商品的最小费用最大流的总和加起来就可以了,注意条件在代码中解释。

注意: 对于源点到供应商这一段不能设置成inf,具体例子如下所示。

s -  >s1,同时s1 - >s2,s1 - > s3。这样的话,就相当于有两条路从s出发,本来s到s1的流量总和为5,但是如果把流量设置成inf,

那么如果本来s1 - >s2,s1 - > s3这两条路的和是超过5的,也就是说按照原来的建图方式,初始从s-> s1 的流量是不够用的,如果改成inf的话,流量就够用,那么就改变题意了。

AC代码:

#include<iostream>
#include<string>
#include<cstring>
#include<iomanip>
#include<cmath>
#include<algorithm>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<stdio.h>
using namespace std;
# define inf 0x3f3f3f3f
# define maxn 150+100
# define ll long long
int need[maxn][maxn];
int store[maxn][maxn];
int Map[maxn][maxn][maxn];// i - > numofstore ,j- > huowu ,k - > keeper
int sum1[maxn],sum2[maxn];
int head[maxn];
int dis[maxn],prev[maxn],pree[maxn];
int vis[maxn];
int num;
struct Edge
{
    int to;
    int cost;
    int w;
    int nex;
} edge[maxn];
void addage(int fr,int to,int w,int cost)
{
    edge[num].to=to;
    edge[num].w=w;
    edge[num].cost=cost;
    edge[num].nex=head[fr];
    head[fr]=num++;
    edge[num].to=fr;
    edge[num].w=0;
    edge[num].cost=-cost;
    edge[num].nex=head[to];
    head[to]=num++;
}
bool spfa(int st,int ed)
{
    memset(vis,0,sizeof(vis));
    memset(pree,-1,sizeof(pree));
    memset(dis,inf,sizeof(dis));
    dis[st]=0;
    vis[st]=1;
    queue<int>q;
    q.push(st);
    while(!q.empty())
    {
        int top=q.front();
        q.pop();
        vis[top]=0;
        for(int i=head[top]; i!=-1; i=edge[i].nex)
        {
            int temp=edge[i].to;
            if(edge[i].w>0&&dis[temp]>dis[top]+edge[i].cost)
            {
                dis[temp]=dis[top]+edge[i].cost;
                pree[temp]=top;
                prev[temp]=i;
                if(vis[temp]==0)
                {
                    vis[temp]=1;
                    q.push(temp);
                }
            }
        }
    }
    return pree[ed]!=-1;
}
int mincostflow(int st,int ed)
{
    int ans=0;
    while(spfa(st,ed))
    {
        int minn=inf;
        for(int i=ed; i!=st; i=pree[i])
        {
            minn=min(minn,edge[prev[i]].w);
        }
        ans+=minn*dis[ed];//dis代表一个单位的花费,minn代表流量。
        for(int i=ed; i!=st; i=pree[i])
        {
            edge[prev[i]].w-=minn;
            edge[prev[i]^1].w+=minn;
        }
    }
    return ans;
}
int main()
{
    int n,m,k;
    while(~scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
    {
        int flag=0;
        memset(need,0,sizeof(need));
        memset(sum1,0,sizeof(sum1));
        memset(sum2,0,sizeof(sum2));
        memset(Map,0,sizeof(Map));
        for(int i=1; i<=n; i++)
        {
            for(int j=1; j<=k; j++)
            {
                scanf("%d",&need[i][j]);
                sum1[j]+=need[i][j];
            }
        }// xu qiu liang
        for(int i=1; i<=m; i++)
        {
            for(int j=1; j<=k; j++)
            {
                scanf("%d",&store[i][j]);
                sum2[j]+=store[i][j];
            }
        }
        for(int i=1; i<=k; i++)
        {
            if(sum1[i]>sum2[i])//判断能否满足构图的条件
            {
                flag=1;
            }
        }
        for(int h=1; h<=k; h++)
        {
            for(int i=1; i<=n; i++)
            {
                for(int j=1; j<=m; j++)
                {
                    scanf("%d",&Map[h][j][i]);// 第j种货物从j仓库到达第i个买家的花费
                }
            }
        }
        if(flag==1)printf("-1\n");
        else
        {
            int ans=0;
            for(int h=1; h<=k; h++)
            {
                memset(head,-1,sizeof(head));
                num=0;
                for(int i=1; i<=m; i++)
                {
                    addage(0,i,store[i][h],0);//注意这里的流量设置,不能设置成inf。
                }
                for(int i=1; i<=m; i++)
                {
                    for(int j=1; j<=n; j++)
                    {
                        addage(i,j+m,inf,Map[h][i][j]);//这里的流量即可以设置成inf,也可以设置成存货。
                    }
                }
                for(int i=1; i<=n; i++)
                {
                    addage(m+i,n+m+1,need[i][h],0);//这里设置成需求量。
                }
                ans+=mincostflow(0,n+m+1);
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值