• 最小费用最大流粗解 poj2516


    最小费用最大流,一般解法如下:

    在流量基础上,每条边还有权费用,即单位流量下的所需费用。在最大流量下,求最小费用。解法:在最大流算法基础上,每次按可行流增广改为每次用spfa按最小费用(用单位费用)增广,每次按每条边一单位费用求到达终点的最小费用(最短路),那么每次找到“最短路”(只是一条路,不是多条(dinic每次可以增广多条)),之后按这条路最大

    可能流量增广(取这条路上残量最小的),直到无法增广为止。(实现细节点代码备注)。

    该题题意:m个供应地向n个商店供应k种物品,对于每种物品,供应地有相应库存量,商店有相应需求量,从供应地j到商店i每单位物品需要路费cost,求满足所有商店的最小费用。

    思路:对于每种商品,若供不应求,则无解。反之:对于每种物品,建立源点0,汇点 n+m+1,。从 0到供应地有边,流量为供应地库存,费用为0,从商店到汇点,流量为该商店需求量,费用为0,从供应地到商店,费用为路费,流量为inf,问题转化为最小费用最大流(满足所有商店,刚刚最大,满流)。



    #include<iostream>
    #include<queue>
    #include<cstdio>
    using namespace std;
    int n,m,k;
    int shop[60][55];int sup[55][55];    //第i个shop/sup 的第j种商品的供求量
    int fire[55][55][55];               //第i种商品从第k个供应商到第j个商店的路费.
    int e[8000][4];int head[105];int nume=0;
    const int inf=0x3f3f3f3f;
    void inline adde(int i,int j,int f,int cost)      //addedge,注意反向边可以费用取反!因为“后悔”时候可以减去费用。
    {
        e[nume][0]=j;e[nume][1]=head[i];head[i]=nume;
        e[nume][2]=f;e[nume++][3]=cost;
        e[nume][0]=i;e[nume][1]=head[j];head[j]=nume;
        e[nume][2]=0;e[nume++][3]=-cost;
    }
    void build(int ii)                    //建图
    {
        for(int i=1+m;i<=n+m;i++)
          adde(i,n+m+1,shop[i-m][ii],0);
        for(int i=1;i<=m;i++)
          adde(0,i,sup[i][ii],0);
        for(int i=1;i<=m;i++)
          for(int j=m+1;j<=m+n;j++)
              adde(i,j,inf,fire[ii][j-m][i]);
    
    }
    int inq[105];int d[105];              //spfa
    bool spfa(int & sum)               //每次求费用
    {
        int pre[105];
        int minf=inf;
        int pr[105];
        for(int i=0;i<n+m+2;i++)
          {
              inq[i]=0;
              d[i]=inf;
          }
        pre[0]=-1 ; pr[0]=-1;       //路径中,分别记录到点i的边,和i之前的点。(这题如果用矩阵建图要方便)
        queue<int>q;
        q.push(0);inq[0]=1;d[0]=0;
        while(!q.empty())
        {
            int cur=q.front();
            q.pop();
            inq[cur]=0;
             for(int i=head[cur];i!=-1;i=e[i][1])
             {
                 int v=e[i][0];
                 if(e[i][2]>0&&e[i][3]+d[cur]<d[v])
                 {
                     d[v]=e[i][3]+d[cur];
                     pr[v]=cur;               //记录增广路
                     pre[v]=i;
                     if(!inq[v])
                     {
                         q.push(v);
                         inq[v]=1;
                     }
                 }
             }
        }
         if(d[n+m+1]==inf)return 0;       //无法增广
         int cur=n+m+1;                    //目的点
         while(cur!=0)             //取路径上最小残流量边作为流量增广
        {
            minf=e[pre[cur]][2]<minf?e[pre[cur]][2]:minf;
            cur=pr[cur];
        }
        cur=n+m+1;
        while(cur!=0)                 //增广,改流量
        {
             e[pre[cur]][2]-=minf;
             e[pre[cur]^1][2]+=minf;
             cur=pr[cur];
        }
       sum+=d[n+m+1]*minf;         //费用为单位费用(该路径下每条边单位流量之和)*流量
       return 1;
    }
    int mincost()               
    {
        int sum=0;
        while(spfa(sum));     //无法增广为止
        return sum;
    }
    int sumshop[55];int sumsup[55];
    int main()
    {
        while(scanf("%d%d%d",&n,&m,&k)&&(n||m||k))
        {
            for(int i=1;i<=k;i++)
                {
                    sumsup[i]=sumshop[i]=0;
                }
    
            for(int i=1;i<=n;i++)
              for(int j=1;j<=k;j++)
              {
                  scanf("%d",&shop[i][j]);
                  sumshop[j]+=shop[i][j];
              }
            for(int i=1;i<=m;i++)
              for(int j=1;j<=k;j++)
               {
                   scanf("%d",&sup[i][j]);
                   sumsup[j]+=sup[i][j];
               }
             for(int i=1;i<=k;i++)
               for(int ii=1;ii<=n;ii++)
                 for(int iii=1;iii<=m;iii++)
                   scanf("%d",&fire[i][ii][iii]);
             int flag=1;
              for(int i=1;i<=k;i++)
                {
                    if(sumsup[i]<sumshop[i])         //供不应求
                    {
                        cout<<-1<<endl;
                        flag=0;
                        break;
                    }
                }
              if(flag==0)continue;     
             int sumcost=0;
             for(int i=1;i<=k;i++)         //每种物品最小费用之和
             {
                 for(int ii=0;ii<n+m+5;ii++)
                      head[ii]=-1;
                  nume=0;
                 build(i);
                 sumcost+=mincost();
             }
            cout<<sumcost<<endl;
        }
    }


  • 相关阅读:
    关于MFC库和CRT库冲突的分析
    C++ Traits技术
    C/C++的参数传递机制
    C++与正态分布
    前端JavaScript
    python 前端 css
    python 前端 html
    MySQL 数据库
    网络编程 生产者消费者模型 GiL

  • 原文地址:https://www.cnblogs.com/yezekun/p/3925715.html
Copyright © 2020-2023  润新知