POj2516 Minimum Cost 最小费用最大流

题目链接:

poj2516





题意:

Dearboy 是一个优秀的食品供应商,他现在面临一个大问题,需要你的帮忙。在他的销售地区,有N 个店主(编号从1~N)帮他销售食品。Dearboy 有M 仓库(编号从1~M),每个仓库第6 章 网络流问题可以提供K 种不同的食品(编号从1~K)。一旦有店主向他订食品,Dearboy 应该安排哪个仓库、向该店主提供多少食品,以减少总的运输费用?
现已知道,从不同的仓库向不同的店主运输不同种类的单位重量食品所需的费用是不同的。给定每个仓库K 种食品格子的储藏量,N 个店主对K 种食品的订量,以及从不同的仓库运输不同种食品到不同的店主的所需费用,你的任务是安排每个仓库的各种食品供应量,以减少总的运输费用。




解题思路:

题目中有 需求数量(容量) , 运输费用(费用)    很容易想到最小费用最大流

但是 题目还给出了一个K 表示品种数量,同时每个品种的需求数量,运输费用各不相同;也就是说如果要构建网络,每条边要抽象为k条边,每条边表示一种费用.orz

重新浏览题目,注意到 题目给出的k很大(50) ,但给出的点很少(50+50+2),

这样我们就可以针对每一个品种,建一次图,跑一次最小最小费用最大流即可.

最后将所有品种的最小费用加起来即可.

考虑答案为-1的情况:仅当某类产品供不应求时才会出现 , 因为给出的k个费用矩阵是全部有值的.



代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 205
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    int to,c,f,w,next;
} edge[maxn*maxn];
int head[maxn],ss;

int supply[55],need[55];
int a[55][55],b[55][55];
int cost[55][55][55];

int dis[maxn],vis[maxn],pre[maxn];
int n,m,k,s,t;

void init()
{
    memset(head,-1,sizeof(head));
    ss=0;
}

void addedge(int a,int b,int c,int d)
{
    edge[ss]=(node){b,c,0,d,head[a]};
    head[a]=ss++;
    edge[ss]=(node){a,c,c,-d,head[b]};
    head[b]=ss++;
}

int spfa()
{
    int q[maxn*10];
    memset(vis,0,sizeof(vis));
    memset(dis,INF,sizeof(dis));
    int u,v,head1=0,tail=0;
    q[tail++]=s;
    dis[s]=0;
    while(head1<tail)
    {
        u=q[head1++];
        vis[u]=0;
        for(int i=head[u]; i!=-1; i=edge[i].next)
        {
            v=edge[i].to;
            if(edge[i].c-edge[i].f>0 && dis[v]>dis[u]+edge[i].w)
            {
                dis[v]=dis[u]+edge[i].w;
                pre[v]=i;
                if(!vis[v])
                {
                    vis[v]=1;
                    q[tail++]=v;
                }
            }
        }
    }
    if(dis[t]<INF)
        return 1;
    return 0;
}

int MCMF()
{
    int minn;
    int sum=0;
    while(spfa())
    {
        minn=INF; //最多能买minn个
        for(int i=t; i!=s; i=edge[pre[i]^1].to)
        {
            if(edge[pre[i]].c-edge[pre[i]].f<minn)
                minn=edge[pre[i]].c-edge[pre[i]].f;
        }
        for(int i=t; i!=s; i=edge[pre[i]^1].to)
        {
            edge[pre[i]].f+=minn;
            edge[pre[i]^1].f-=minn;
        }
        sum+=minn*dis[t];
    }
    return sum;
}

void build(int kind)
{
    init();
    for(int i=1; i<=m; i++)
        addedge(s,i,b[i][kind],0);
    for(int j=1; j<=n; j++)
        addedge(j+m,t,a[j][kind],0);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++)
            addedge(j,m+i,INF,cost[kind][i][j]);
}

int main()
{
//    freopen("in.txt","r",stdin);
    while(scanf("%d%d%d",&n,&m,&k)&&(n+m+k))
    {
        s=0,t=n+m+1;
        int flag=0,ans=0;
        memset(supply,0,sizeof(supply));
        memset(need,0,sizeof(need));
        for(int i=1; i<=n; i++)
            for(int j=1; j<=k; j++)
            {
                scanf("%d",&a[i][j]);
                need[j]+=a[i][j];
            }
        for(int i=1; i<=m; i++)
            for(int j=1; j<=k; j++)
            {
                scanf("%d",&b[i][j]);
                supply[j]+=b[i][j];
            }
        for(int i=1; i<=k; i++)
            if(supply[i]<need[i])   //供不应求
                flag=1;
        for(int kk=1; kk<=k; kk++)
            for(int i=1; i<=n; i++)
                for(int j=1; j<=m; j++)
                    scanf("%d",&cost[kk][i][j]);
        if(flag)
        {
            printf("-1\n");
            continue;
        }
        for(int i=1; i<=k; i++)  //建k次图 跑k次最小费用最大流 orz
        {
            build(i);
            ans+=MCMF();
        }
        printf("%d\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值