度度熊的交易计划 HDU - 6118 (费用流)

度度熊参与了喵哈哈村的商业大会,但是这次商业大会遇到了一个难题:

喵哈哈村以及周围的村庄可以看做是一共由n个片区,m条公路组成的地区。

由于生产能力的区别,第i个片区能够花费a[i]元生产1个商品,但是最多生产b[i]个。

同样的,由于每个片区的购买能力的区别,第i个片区也能够以c[i]的价格出售最多d[i]个物品。

由于这些因素,度度熊觉得只有合理的调动物品,才能获得最大的利益。

据测算,每一个商品运输1公里,将会花费1元。

那么喵哈哈村最多能够实现多少盈利呢?
Input
本题包含若干组测试数据。
每组测试数据包含:
第一行两个整数n,m表示喵哈哈村由n个片区、m条街道。
接下来n行,每行四个整数a[i],b[i],c[i],d[i]表示的第i个地区,能够以a[i]的价格生产,最多生产b[i]个,以c[i]的价格出售,最多出售d[i]个。
接下来m行,每行三个整数,u[i],v[i],k[i],表示该条公路连接u[i],v[i]两个片区,距离为k[i]

可能存在重边,也可能存在自环。

满足:
1<=n<=500,
1<=m<=1000,
1<=a[i],b[i],c[i],d[i],k[i]<=1000,
1<=u[i],v[i]<=n
Output
输出最多能赚多少钱。
Sample Input
2 1
5 5 6 1
3 5 7 7
1 2 1
Sample Output
23

思路:最短路小于0是才跑流量,大于等于0时结束。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define ll long long
#define inf 0x3f3f3f3f

using namespace std;
int a,b,c,d,n,m,u,v,w,s,t,num,N;
struct edge
{
    int next,to,cap,flow,cost;
}e[6100];//500*2*2+1000*2*2
int head[505],pre[505],dis[505],vis[505];

void add(int from,int to,int cap,int cost)
{//拆点     反向弧
    e[num].next=head[from];
    e[num].to=to;
    e[num].cap=cap;
    e[num].flow=0;
    e[num].cost=cost;
    head[from]=num++;
    e[num].next=head[to];
    e[num].to=from;
    e[num].cap=0;
    e[num].flow=0;
    e[num].cost=-cost;
    head[to]=num++;
}
int spfa(int s,int t)
{
    queue<int> q;
    memset(dis,inf,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(pre,-1,sizeof(pre));//注意
    //每个点的前一条边的编号
    vis[s]=1;
    dis[s]=0;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();
        q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int v=e[i].to;
            if(e[i].cap>e[i].flow&&dis[v]>dis[now]+e[i].cost)
            {
                dis[v]=dis[now]+e[i].cost;
                pre[v]=i;//边的编号
                if(!vis[v])
                {
                    vis[v]=1;
                    q.push(v);
                }
            }
        }
    }
    return pre[t]!=-1;
}
int MCMF()//逆向更新
{
    int res=0;
    while(spfa(s,t))
    {//i是边的编号
        int mi=inf;//取得最小值
        for(int i=pre[t];i!=-1;i=pre[e[i^1].to])
        {
            if(mi>e[i].cap-e[i].flow)//容量流量之差
            {
                mi=e[i].cap-e[i].flow;
            }
        }
        //i是前一条边
        for(int i=pre[t];i!=-1;i=pre[e[i^1].to])
        {
            e[i].flow+=mi;//逆向加
            e[i^1].flow-=mi;//正向减 负负得正
        }
        if(dis[t]>=0)
            return res;
        res+=dis[t]*mi;//dis[t]费用,mi流量
    }
    return res;
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    while(cin >> n >> m)
    {
        s=0,t=n+1;
        N=n+2;
        memset(head,-1,sizeof(head));
        num=0;
        for(int i=1;i<=n;i++)
        {
            cin >> a >> b >> c >> d;
            add(s,i,b,a);//超级源点
            add(i,t,d,-c);//超级汇点
        }
        while(m--)
        {
            cin >> u >> v >> w;
            add(u,v,inf,w);
            add(v,u,inf,w);//无向边
        }
        cout << -MCMF() << endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值