HDU 6118 度度熊的交易计划 (最小费用流变形)

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

喵哈哈村以及周围的村庄可以看做是一共由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

Source
2017”百度之星”程序设计大赛 - 初赛(B)

思路:构建一个源点s和一个汇点t,生产数量和卖出数量看做cap,单价当做cost,与源点链接时费用取负,因为是生产,与汇点链接时费用取正,然后跑一下最小费用流的板子,不过要注意这里要把dis设为-inf,然后每次去最长的路,因为cost代表挣到的钱,当然是挣得越多越好,如果到汇点的路dis为负数时要跳出。

代码如下:

#include<bits/stdc++.h>

using namespace std;
const int MAX_V = 510;
const int MAX_E = 1010;
const int INF = 0x3f3f3f3f;

struct Edge{
    int to,cap,cost,rev;
};
vector<Edge> G[MAX_V];
int V,E;
void add(int u,int v,int cap,int cost){
    G[u].push_back((Edge){v,cap,cost,(int)G[v].size()});
    G[v].push_back((Edge){u,0,-cost,(int)G[u].size()-1});
}
int dis[MAX_V],prevv[MAX_V],preve[MAX_V];
bool used[MAX_V];

int min_cost_flow(int s,int t){
    int res = 0;
    while(true){
        for(int i=0;i<=V+1;i++)
            dis[i] = -INF;
        memset(used,false,sizeof(used));
        memset(prevv,-1,sizeof(prevv));
        memset(preve,-1,sizeof(preve));
        queue<int> q;
        dis[s] = 0;
        q.push(s);
        used[s] = true;
        while(!q.empty()){
            int v = q.front();q.pop();
            used[v] = false;
            for(int i=0;i<G[v].size();i++){
                Edge &e = G[v][i];
                if(e.cap > 0 && dis[e.to] < dis[v] + e.cost){
                    dis[e.to] = dis[v] + e.cost;
                    prevv[e.to] = v;//用来记录前趋点
                    preve[e.to] = i;
                    if(!used[e.to]){
                        used[e.to] = true;
                        q.push(e.to);
                    }
                }
            }
        }
        if(dis[t] < 0)  return res;//这里要跳出
        int d = INF;
        for(int v = t; v != s;v = prevv[v]){
            d = min(d,G[prevv[v]][preve[v]].cap);
        }//取最小的cap
        res += d*dis[t];
        for(int v = t;v != s;v = prevv[v]){
            Edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
}
void init(){
    for(int i=0;i<=V+1;i++)
        G[i].clear();
}
int main(void){
    while(scanf("%d %d",&V,&E) != EOF){
        init();
        int a,b,c,d;
        int s = 0,t = V+1;
        for(int i=1;i<=V;i++){
            scanf("%d %d %d %d",&a,&b,&c,&d);
            add(s,i,b,-a);add(i,t,d,c);
        }
        int x,y,z;
        for(int i=1;i<=E;i++){
            scanf("%d %d %d",&x,&y,&z);
            add(x,y,INF,-z);
            add(y,x,INF,-z);
        }
        int res = min_cost_flow(s,t);
        printf("%d\n",res);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值