UVA 11613 Acme Corporation(不固定流量的最小费用流)

题目:http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2660
题目大意:现在一个公司生产销售X元素。M个月,每个月都有5个东西,表示这个月的每单元生产成本、最大生产量、销售单价、当月最大销售量、这个生产的东西的最大储存时间(这个月不算)。还给你一个整数,表示每单元产品留存放一个月的代价。问你,这M个月最大收益是多少?
解题思路:左边 m 个点表示m个生产月,右边 m 个点表示 m 个销售月,再加两个点,源点和汇点。从源点连左边的m个点,cap 为 最大生产量,cost 为成本。再从右边m个点分别连边到 汇点,cap 为 最大销售量,cost 为 负的单价。然后由于第 i 个月生产的东西可以存几个月,那么就从 左边每个点,分别按照可以储存的月数往右边连边,cap 为 INF,cost 为 存放一个月的代价*存放的月数。然后很清楚,求最大收益,那么就是求整张图的最小费用流,由于并没有要求满载,那么流量就是不固定的,即不固定流量的最小费用流。
    不固定的流量的最小费用流,可以用最小费用最大流来解,只需要当 s-t 的增光路长度(即 d[ t ] )> 0 时,return 0 (即不需要再增广了)就 OK 了。
    另外,这道题 cost 会超 int ,要用 long long 。
    第一道最小费用流,建模自己YY了一点,大体是出来了,但是对费用这个概念比较模糊,完整的模型并没有抽象出来。。   最后由于 long long的原因 WA 了几发,看了人家博客,才发现原来是 long long 的关系。。 = =

代码如下:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;

const int INF = 0x0fffffff;
const int MAXN = 222;
const int MAXM = MAXN*MAXN;

typedef long long lld;

struct Edge
{
    int s,t,cap,flow,cost,next;
} edge[MAXM];

int tot,head[MAXN];

void edge_init()
{
    tot = 0;
    memset(head,-1,sizeof(head));
}

void add_edge(int s,int t,int cap,int cost)
{
    edge[tot].s = s;
    edge[tot].t = t;
    edge[tot].cap =cap;
    edge[tot].flow = 0;
    edge[tot].cost = cost;
    edge[tot].next = head[s];
    head[s] = tot++;

    edge[tot].s = t;
    edge[tot].t = s;
    edge[tot].cap = 0;
    edge[tot].flow = 0;
    edge[tot].cost = -cost;
    edge[tot].next = head[t];
    head[t] = tot++;
}

struct MCMF
{
    int s,t,n;
    int d[MAXN],p[MAXN],a[MAXN];
    bool inq[MAXN];

    void init(int n)
    {
        this->n = n;
    }

    int spfa(lld& flow,lld& cost)
    {
        for(int i = 0;i < n;i++) d[i] = INF,inq[i] = 0;
        d[s] = 0;inq[s] = 1;a[s] = INF;
        queue<int> q;
        q.push(s);
        while(!q.empty())
        {
            int x = q.front();
            q.pop();
            inq[x] = 0;
            for(int i = head[x];i != -1;i = edge[i].next)
            {
                int to = edge[i].t;
                if(edge[i].cap > edge[i].flow && d[to] > d[x]+edge[i].cost)
                {
                    d[to] = d[x]+edge[i].cost;
                    p[to] = i;
                    a[to] = min(a[x],edge[i].cap-edge[i].flow);
                    if(!inq[to])
                    {
                        q.push(to);
                        inq[to] = 1;
                    }
                }
            }
        }
        if(d[t] > 0) return 0;
        flow += a[t];
        cost += (lld)d[t]*a[t];
        int x = t;
        while(x != s)
        {
            edge[p[x]].flow += a[t];
            edge[p[x]^1].flow -= a[t];
            x = edge[p[x]].s;
        }
        return 1;
    }

    lld min_cost(int s,int t)
    {
        this->s = s;this->t = t;
        lld flow = 0,cost = 0;
        while(spfa(flow,cost));
        return cost;
    }
} sol;

struct Month
{
    int cost,pro,price,sale,t;
    void read()
    {
        scanf("%d%d%d%d%d",&cost,&pro,&price,&sale,&t);
    }
} month[111];

int nn;
int id[3][111];

void get_id_init()
{
    memset(id,0,sizeof(id));
    nn = 0;
}

int get_id(int i,int j)
{
    if(id[i][j]) return id[i][j];
    else return id[i][j] = nn++;
}

int main()
{
    int cas = 0 ;
    int _;
    scanf("%d",&_);
    while(_--)
    {
        int m,c;
        scanf("%d%d",&m,&c);
        for(int i = 0;i < m;i++)
        {
            month[i].read();
        }
        get_id_init();
        int s = get_id(0,0);
        int t = get_id(0,1);
        edge_init();
        for(int i = 0;i < m;i++)
        {
            add_edge(s,get_id(1,i),month[i].pro,month[i].cost);
            add_edge(get_id(1,i),get_id(2,i),month[i].pro,0);
            for(int j = 1;j <= month[i].t && (i+j) < m;j++)
            {
                add_edge(get_id(1,i),get_id(2,i+j),month[i].pro,c*j);
            }
        }
        for(int i = 0;i < m;i++)
            add_edge(get_id(2,i),t,month[i].sale,-month[i].price);
        sol.init(nn);
        printf("Case %d: %lld\n",++cas,-sol.min_cost(s,t));
    }
    return 0;
}


/*
1
2 2
2 10 3 20 2
10 100 7 5 2

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值