最小费用最大流(模板)

转自:http://blog.csdn.net/axuan_k/article/details/47322401


解题思路:

按照最小费用最大流的思想:  

每条边的容量表示可以经过的次数 ,每条边的费用表示经过这条边所需的费用

那么题目中的每条已给出的边 可以拆成两条容量为1的边:费用分别为di  di+ai

将源点与1连接   n与汇点连接  容量都为2  费用为0

这样跑出来的最小费用 就是答案所求

原理其实就是通过最大流中的反向边 纠正路线。

这样即使第一次走的不是全局最优的,但可以通过第二次走反向边纠正过来

最后得出全局最优解


用了邻接表,观察好head就可以了,把head当做链表


代码:


#include<iostream>

#include<cstdio>

#include<cstring>

#include<queue>

#define INF 0x3f3f3f

#define maxn 1005

using namespace std;

struct node{

    int to,f,v,next;

}edge[maxn*10];

int head[maxn],ss;

int pre[maxn];

int n,s,t;

int ans;


void init()

{

    ss=0;

    ans=0;

    memset(head,-1,sizeof(head));

    memset(pre,0,sizeof(pre));

}


void addedge(int a,int b,int c,int w)

{

    edge[ss]=(node){b,c,w,head[a]};

    head[a]=ss++;

    edge[ss]=(node){a,0,-w,head[b]};

    head[b]=ss++;

}


int spfa()

{

    int vis[maxn]={0};

    int dis[maxn];

    memset(dis,INF,sizeof(dis));

    queue<int>q;

    int u,v;

    q.push(s);

    dis[s]=0;

    

    while(!q.empty())

    {

        u=q.front();

        q.pop();

        vis[u]=0;

        for(int i=head[u];i!=-1;i=edge[i].next)

        {

            v=edge[i].to;

            if(edge[i].f &&  dis[v]>dis[u]+edge[i].v)

            {

                dis[v]=dis[u]+edge[i].v;

                pre[v]=i;

                if(!vis[v])

                {

                    vis[v]=1;

                    q.push(v);

                }

            }

        }

    }

    if(dis[t]<INF){

        ans+=dis[t];

        return1;

    }

    return0;

}


void MCMF()

{

    int d;

    while(spfa())      //能跑到汇点

    {

        for(int i=t;i!=s;i=edge[pre[i]^1].to)

        {

            edge[pre[i]].f--;;

            edge[pre[i]^1].f++;

        }

    }

}

int main()

{

    //    freopen("in.txt","r",stdin);

    int m,a,b,c,d;

    int Case=1;

    while(~scanf("%d%d",&n,&m))

    {

        init();

        s=0,t=n+1;

        addedge(s,1,2,0);

        addedge(n,t,2,0);

        while(m--)

        {

            scanf("%d%d%d%d",&a,&b,&c,&d);

            addedge(a,b,1,c);

            addedge(a,b,1,c+d);   //拆为两条容量为1的边

        }

        MCMF();

        printf("Case %d: %d\n",Case++,ans);

    }

    return0;

}


/**********************

早晨大概九点十点的时候或者在楼下买烧卖黑米粥吃,或者买土豆卷豆浆吃,如果懒得买就在起床后烧开水泡燕麦牛奶粥填肚子。中午或者提前叫外卖,可以买咖喱牛肉饭,土豆排骨饭,今天中午吃的鱼豆腐炒肉饭,可以买鸡丁加土豆丝炒肉饭,烤肉饭,孜然土豆丝饭诸如此类,楼下有便宜又好吃的铁板鱼卖,土豆是我的最爱。晚上可以去学校旁边的小市场买各种小吃,比较喜欢狼牙土豆和炒酸奶,还有烤面筋。

看了村上年轻时候的食谱,他去各种名字有格调的小店吃牛排喝鸡尾酒咖啡看书,想来也不是很贵,只是和我年轻的食谱完全不一样,鸡尾酒咖啡对我来说都是一两年喝不了一次的非日常饮料,不过偶尔会买罐装的RIO,牛排作为午餐太单调,毕竟没有土豆做配菜。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值