POJ 2146 Minimum Cost KM/最小费用流

http://poj.org/problem?id=2516

题意:N个批发商,M个供应商,K种商品,每个批发商每种商品都需要一定的数量,每个供应商每种商品都提供一定的数量。

    每个供应商向每个批发商运送某种单位数量的商品

KM  思路:每种商品 需要a个,那x边就有a个点,该种商品供应有b个,那y边就有b个点,

 权值为,运费。。。说的有点乱。就是我看了别人的这种思路之后,输入还是没搞定。。。这输入也挺有技巧的,我是

这样觉得   匈牙利算法时间复杂度O(ne) ,KM每找到一条增广路径要顶标调整,复杂度O(ne^2) 不知道我这个KM的复杂度

是不是那么小

代码(KM):

#include<iostream>
#include<cstdio>
#include<string>
#include<cstring>
#define inf 1<<30
#define Max(a,b)a>b?a:b
#define Min(a,b)a<b?a:b
#define nMAX 155
using namespace std;

int map[nMAX][nMAX],link[nMAX],lx[nMAX],ly[nMAX];
bool vx[nMAX],vy[nMAX];
int n,m;

bool dfs(int u)
{
    int j;
    vx[u]=1;
    for(j=1;j<=m;j++)
       if(!vy[j]&&map[u][j]==lx[u]+ly[j])//!!!map[u][j]==lx[u]+ly[j]
       {
           vy[j]=1;
           if(!link[j]||dfs(link[j]))
              {
                  link[j]=u;
                  return true;
              }
       }
     return false;
}

int KM()
{
    int i,j,k;
    for(i=1;i<=n;i++)
    {   lx[i]=-inf;
       for(j=1;j<=m;j++)
         lx[i]=Max(lx[i],map[i][j]);//每个map[i][j]都已初始化
    }
    memset(ly,0,sizeof(ly));

    memset(link,0,sizeof(link));
    for(i=1;i<=n;i++)
    {
        while(1)
        {
            memset(vx,0,sizeof(vx));
            memset(vy,0,sizeof(vy));
            if(dfs(i))break;
            //修改 lx ly
            int MIN=inf;

            for(j=1;j<=n;j++)
              if(vx[j])
                for(k=1;k<=m;k++)
                 if(!vy[k])
                   MIN=Min(MIN,lx[j]+ly[k]-map[j][k]);

            for(j=1;j<=n;j++)if(vx[j])lx[j]-=MIN;//lx[j]--;错错错
            for(j=1;j<=m;j++)if(vy[j])ly[j]+=MIN;
        }
    }
    int ANS=0;
    for(i=1;i<=m;i++)
       if(link[i]) ANS+=map[link[i]][i];
    return ANS;
}

int main()
{
    int cn[55][nMAX],cm[55][nMAX],com1[55],com2[55],f[55][55];
    int N,M,K;
    int i,j,h,cnt;
    while(scanf("%d%d%d",&N,&M,&K))
    {
        if(N==0&&M==0&&K==0)break;

        memset(com1,0,sizeof(com1));
        memset(com2,0,sizeof(com2));
       // memset(cn,0,sizeof(cn));
       // memset(cm,0,sizeof(cm));
        for(i=1;i<=N;i++)
         for(j=1;j<=K;j++)
         {
             scanf("%d",&cnt);
             while(cnt--)
             cn[j][++com1[j]]=i; //第com1[j]件第j中商品,第i个shop需要
         }

        for(i=1;i<=M;i++)
          for(j=1;j<=K;j++)
          {
              scanf("%d",&cnt);
              while(cnt--)
              cm[j][++com2[j]]=i; //同理
          }

        bool flag=1;
        int ans=0;
        for(h=1;h<=K;h++)
        {
            for(i=1;i<=N;i++)
             for(j=1;j<=M;j++)
                 scanf("%d",&f[i][j]);

            if(com1[h]>com2[h])
            {
                flag=0;
                continue;
            }
            memset(map,0,sizeof(map));
            for(i=1;i<=com1[h];i++)
             for(j=1;j<=com2[h];j++)
             {
                map[i][j]=-f[cn[h][i]][cm[h][j]];
             }
             n=com1[h];
             m=com2[h];
             ans-=KM();
        }
        if(!flag)printf("%d\n",-1);
        else printf("%d\n",ans);
    }
    return 0;
}

 最小费用最大流

思路:因为涉及到K种商品,所以 考虑到 N*K个需求者,M*K个供应者,就是一种需求者只需求一种产品,一种供应者只提供一种产品;需求第i 个产品的需求者 向提供第i的供应真连线,cap=inf,cost=运费,源点到需求者连线 cap=需求量,cost=0 ,供应者到汇点连线,cap=提供量,cost=0。。。就是基于把每个需求者分成K个需求者,把每个供应者分成K个供应者。。。结果TLE,囧  时间复杂度 最坏有2500+2500+2个点,(2500*50+2500*2)*2=26*10^4 条边 ,费用流的时间复杂度是O(2*M*C)吧?!好像是,m是边,C是流量3 , O(2*M)是spfa的复杂度, 最后用时 26*10^4*2*2500*3=3.9*10^9  怪不得超时

然后网上是单独的算出每种产品的费用,因为每种产品是相互独立的,然后加起来,那时间复杂度是多少呢?每一种产品 最坏情况用50+50+2个点,(50*50+50*2)*2=5200条边  O(2*m*c)=1.56*10^6  ,50种产品就有  50*1.56*10^6=7.8*10^7

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#define nMAX 110
#define mMAX 6000
#define inf 1<<26
using namespace std;
int s,n;
int head[nMAX],s_edge,ans,dis[nMAX],pre[nMAX],qu[nMAX],num;
int need[55][55],provide[55][55],cntn[55],cntp[55];
bool vs[nMAX];
struct Edge
{
    int u,v,cap,cost,nxt;
}edge[mMAX];

void addedge(int u,int v,int cap,int cost)
{
    s_edge++;
    edge[s_edge].v=v;
    edge[s_edge].cap=cap;
    edge[s_edge].cost=cost;
    edge[s_edge].nxt=head[u];
    head[u]=s_edge;

    s_edge++;
    edge[s_edge].v=u;
    edge[s_edge].cap=0;
    edge[s_edge].cost=-cost;
    edge[s_edge].nxt=head[v];
    head[v]=s_edge;
}
bool spfa()
{
    int i,beg,tail;
    for(i=0;i<=n;i++)
    {
        dis[i]=inf;
        vs[i]=0;
    }
    beg=0,tail=1;
    dis[s]=0;
    vs[s]=1;
    qu[0]=s;
    while(beg!=tail)
    {
        int u=qu[beg++];
        for(int e=head[u];e;e=edge[e].nxt)
        {
            int v=edge[e].v;
            if(edge[e].cap&&dis[v]-dis[u]>edge[e].cost)
            {
                dis[v]=dis[u]+edge[e].cost;
                pre[v]=e;
                if(!vs[v])
                {
                    vs[v]=1;
                    qu[tail++]=v;
                    if(tail==nMAX)tail=0;
                }
            }
        }
        vs[u]=0;
        if(beg==nMAX)beg=0;
    }
   if(dis[n]!=inf)return 1;
   return 0;
}
void end()
{
    int u,p;
    int MIN=inf;
    for(u=n;u!=s;u=edge[p^1].v)
    {
        p=pre[u];
        MIN=MIN<edge[p].cap?MIN:edge[p].cap;

    }
    num+=MIN;
    for(u=n;u!=s;u=edge[p^1].v)
    {
        p=pre[u];
        edge[p].cap-=MIN;
        edge[p^1].cap+=MIN;
        ans+=(edge[p].cost*MIN);
    }
}
int main()
{
    int N,M,K,i,j,f,w;
    while(~scanf("%d%d%d",&N,&M,&K))
    {
        if(N==0&&M==0&&K==0)break;
        memset(cntn,0,sizeof(cntn));
        memset(cntp,0,sizeof(cntp));
        for(i=1;i<=N;i++)
            for(j=1;j<=K;j++)
            {
                scanf("%d",&need[i][j]);
                cntn[j]+=need[i][j];
            }
        for(i=1;i<=M;i++)
            for(j=1;j<=K;j++)
            {
                 scanf("%d",&provide[i][j]);
                 cntp[j]+=provide[i][j];
            }
        bool fg=1;
        ans=0;
        for(i=1;i<=K;i++)
        {  
            s_edge=1;
            memset(head,0,sizeof(head));
            s=0,n=N+M+1;
            for(j=1;j<=N;j++)
                for(f=1;f<=M;f++)
                {
                    scanf("%d",&w);
                    addedge(j,N+f,inf,w);
                }
            if(cntp[i]<cntn[i]){fg=0;continue;}
            for(j=1;j<=N;j++)
                addedge(s,j,need[j][i],0);
            for(j=1;j<=M;j++)
                addedge(N+j,n,provide[j][i],0);
            num=0;
            if(fg)
                while(spfa())
                {
                    end();
                }
            if(num<cntn[i])fg=0;//不能break啊,数据读不完就WA!!!^ ^
        }
        if(fg)
            printf("%d\n",ans);
        else
            printf("-1\n");
    }
    return 0;
}

  

 

  

转载于:https://www.cnblogs.com/sdau10kuaile/archive/2012/02/23/2364105.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值