hdu 6118 度度熊的交易计划

度度熊的交易计划

Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 993    Accepted Submission(s): 360


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
 
题意:每个地方都能生产物品或者接收其他地方的物品并且负责卖出这些物品,求最大利润
思路:最小费用流,每个地区拆成两个点,一个生产点,一个卖出点,源点s到每个生产点连一条容量为bi,费用为0的边,代表每个生产点可以生产的物品数量;卖出点向汇点t连一条容量为di,费用为0的边,代表每个点最多能卖di个物品;中间的绿线容量为INF,费用为-(c[j]-a[i]-d[i][j]),即利润的相反数,代表生产点i将物品给卖出点j所能带来的利润值,这个利润值必须是正数,否则亏钱了。当不需要再生产时地区也可以选择不继续生产,这时则要连一条黄线。
很多人会觉得黄线比较多余,当然我起初也是这么想的,但这样能找到反例,而黄线的作用在于可以修改算法之前所犯下的一些错误,可以看下面的图:

 


 

这个时候最好的路径选择应该是:s->1->3->t,   s->2->4->t 两条,利润为10。但是如果程序一开始选择了路径s->1->4->t 呢,这也是当前的一条最短路径,这时要是没有黄线,下一次从s->2之后无路可走了,利润就变成5了。。。
如果黄线存在,就可以修改上述程序已经出现的错误,考虑选择了s->1->4->t后的残余网络,这时有了一条流量为1的反向路径t->4->1->s,那么通过黄线就有了s->2->4->1->t这条路,修改了之前的错误,这时又可以走s->1->3->t这条路径了。


#define _CRT_SECURE_NO_DEPRECATE
#include<iostream>
#include<algorithm>
#include<queue>
#include<set>
#include<vector>
#include<cstring>
#include<string>
#include<functional>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
const int N_MAX = 500 + 4, V_MAX = 1000 + 10;

struct Edge
{
    int from, to, cap, flow, cost;
    Edge(int u, int v, int ca, int f, int co) :from(u), to(v), cap(ca), flow(f), cost(co) {};
};

struct MCMF
{
    int n, m, s, t;
    vector<Edge> edges;
    vector<int> G[V_MAX];
    int inq[V_MAX];//是否在队列中
    int d[V_MAX];//距离
    int p[V_MAX];//上一条弧
    int a[V_MAX];//可改进量

    void init(int n)//初始化
    {
        this->n = n;
        for (int i = 0; i<n; i++)
            G[i].clear();
        edges.clear();
    }

    void add_edge(int from, int to, int cap, int cost)//加边
    {
        edges.push_back(Edge(from, to, cap, 0, cost));
        edges.push_back(Edge(to, from, 0, 0, -cost));
        int m = edges.size();
        G[from].push_back(m - 2);
        G[to].push_back(m - 1);
    }

    bool SPFA(int s, int t, int &flow, int &cost)//寻找最小费用的增广路,使用引用同时修改原flow,cost
    {
        for (int i = 0; i<n; i++)
            d[i] = INF;
        memset(inq, 0, sizeof(inq));
        d[s] = 0; inq[s] = 1; p[s] = 0; a[s] = INF;
        queue<int> Q;
        Q.push(s);
        while (!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            inq[u]--;
            for (int i = 0; i<G[u].size(); i++)
            {
                Edge& e = edges[G[u][i]];
                if (e.cap>e.flow && d[e.to]>d[u] + e.cost)//满足可增广且可变短
                {
                    d[e.to] = d[u] + e.cost;
                    p[e.to] = G[u][i];
                    a[e.to] = min(a[u], e.cap - e.flow);//e.flow是这条边已经用掉了的流量
                    if (!inq[e.to])
                    {
                        inq[e.to]++;
                        Q.push(e.to);
                    }
                }
            }
        }
        if (d[t] == INF) return false;//汇点不可达则退出
        flow += a[t];
        cost += d[t] * a[t];
        int u = t;
        while (u != s)//更新正向边和反向边
        {
            edges[p[u]].flow += a[t];
            edges[p[u] ^ 1].flow -= a[t];
            u = edges[p[u]].from;
        }
        return true;
    }

    int MincotMaxflow(int s, int t)
    {
        int flow = 0, cost = 0;
        while (SPFA(s, t, flow, cost));
        return cost;
    }
};

int n, m;
int a[N_MAX], b[N_MAX], c[N_MAX], D[N_MAX];
int d[N_MAX][N_MAX];

void floyd() {
    for (int k = 1; k <= n; k++)
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}

int main() {
    
    while (scanf("%d%d",&n,&m)!=EOF) {
        memset(d,INF,sizeof(d));
        for (int i = 1; i <= n; i++)d[i][i] = 0;
        int s = 0, t =2* n + 1;
        int V = t + 1;
        MCMF mcmf;
        mcmf.init(V);
        //1~n:生产点
        //n+1~2n:出售点
        
        for (int i = 1; i <= n;i++) {
            scanf("%d%d%d%d",&a[i],&b[i],&c[i],&D[i]);
            mcmf.add_edge(s,i,b[i],0);//最多生产b[i]个
            mcmf.add_edge(i+n,t,D[i],0);//最多出售D[i]个
        }
        for (int i = 1; i <= m;i++) {
            int u, v, w;
            scanf("%d%d%d",&u,&v,&w);
            if (u == v)continue;//!!!!!!自环
            d[u][v] = min(d[u][v],w);//!!!!重边
            d[v][u] = min(d[v][u], w);
           }
        floyd();
        for (int i = 1; i <= n;i++) {
            for (int j = 1; j <= n;j++) {
                if (d[i][j] == INF)continue;
                int profit= c[j] - a[i] - d[i][j];
                if (profit > 0) {
                    mcmf.add_edge(i, j + n, INF, -profit);
                }
            }
            mcmf.add_edge(i,t,b[i],0);//!!
        }
        printf("%d\n",-mcmf.MincotMaxflow(s,t));
    }

    return 0;
}

 


 


 


 

posted on 2017-08-16 16:08  ZefengYao  阅读(257)  评论(0编辑  收藏  举报

导航