#bzoj2375#餐巾计划问题(费用流建图经典模型)

2375: 【网络流】餐巾计划问题

时间限制: 1 Sec  内存限制: 128 MB

题目描述

一个餐厅在相继的N 天里,每天需用的餐巾数不尽相同。假设第i天需要ri块餐巾(i=1,2,…,N)。餐厅可以购买新的餐巾,每块餐巾的费用为p分;或者把旧餐巾送到快洗部,洗一块需m天,其费用为f 分;或者送到慢洗部,洗一块需n 天(n>m),其费用为s<f 分。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好N 天中餐巾使用计划,使总的花费最小。

输入

文件第1 行有6 个正整数N,p,m,f,n,s。N 是要安排餐巾
使用计划的天数;p 是每块新餐巾的费用;m 是快洗部洗一块餐巾需用天数;f 是快洗部洗
一块餐巾需要的费用;n是慢洗部洗一块餐巾需用天数;s是慢洗部洗一块餐巾需要的费用。
接下来的N 行是餐厅在相继的N 天里,每天需用的餐巾数。

输出

程序运行结束时,将餐厅在相继的N 天里使用餐巾的最小总花费输出到文件output.txt中。

样例输入

3 10 2 3 3 2
5
6
7

样例输出

145

建立一个二分图,X部Y部各N个点,

Xi表示第i天用掉的餐巾数,从Src连边至Xi边的容量为x(即题中给出的每天要用的数量),

Yi表示第i天之前至少要准备好的(即第i天需要用的)餐巾数,从Yi连边至Tar边的容量为x

对于Xi,

可以选择将Xi中的移至快洗部Xi -> Yi + m,容量无限,费用f,

可以移至慢洗部Xi -> Yi + n,容量无限,费用s,

还可以留下来到下一天Xi -> Xi + 1,容量无限,费用0

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<queue>
using namespace std;
 
const int Max = 2005;
const int Maxm = 100005;
const int INF = 0x3f3f3f3f;
 
struct node{
    int v, cap, cost, nxt;
}edge[Maxm << 2];
 
int N, p, m, f, n, s;
int cnt = 1, Src, Tar, Flow, Cost;
int fir[Max], Dis[Max];
bool vis[Max], Inq[Max];
 
void getint(int & num){
    char c; int flg = 1;    num = 0;
    while((c = getchar()) < '0' || c > '9')   if(c == '-')    flg = -1;
    while(c >= '0' && c <= '9'){  num = num * 10 + c - 48;    c = getchar();}
    num *= flg;
}
 
void addedge(int a, int b, int c, int d){
    edge[++ cnt].v = b, edge[cnt].cap = c, edge[cnt].cost = d, edge[cnt].nxt = fir[a], fir[a] = cnt;
    edge[++ cnt].v = a, edge[cnt].cap = 0, edge[cnt].cost = -d, edge[cnt].nxt = fir[b], fir[b] = cnt;
}
 
int Aug(int i, int augco){
    if(i == Tar){
        Cost += Dis[Tar] * augco;
        return augco;
    }
    int augc = augco, delta;
    for(int j = fir[i]; j && augc; j = edge[j].nxt) if(edge[j].cap && ! vis[edge[j].v])
        if(Dis[i] == Dis[edge[j].v] - edge[j].cost){
            vis[edge[j].v] = 1;
            delta = Aug(edge[j].v, min(augc, edge[j].cap));
            edge[j].cap -= delta;
            edge[j ^ 1].cap += delta;
            augc -= delta;
        }
    return augco - augc;
}
 
queue<int>Q;
bool SPFA(){
    memset(Dis, 0x3f, sizeof(Dis));
    memset(Inq, 0, sizeof(Inq));
    Dis[Src] = 0;
    Q.push(Src);
    while(! Q.empty()){
        int tmp = Q.front();    Q.pop();
        Inq[tmp] = 0;
        for(int j = fir[tmp]; j; j = edge[j].nxt)
            if(edge[j].cap && Dis[edge[j].v] > Dis[tmp] + edge[j].cost){
                Dis[edge[j].v] = Dis[tmp] + edge[j].cost;
                if(! Inq[edge[j].v])    Inq[edge[j].v] = 1, Q.push(edge[j].v);
            }
    }
    return Dis[Tar] != Dis[Tar + 1];
}
 
int MCMF(){
	Flow = 0, Cost = 0;
	while(SPFA()){
		memset(vis, 0, sizeof(vis));
		vis[Src] = 1;
		Flow += Aug(Src, INF);
	}
	return Cost;
}

int  main(){
    getint(N), getint(p), getint(m), getint(f), getint(n), getint(s);
	Src = 0, Tar = N << 1| 1;
	int x;
	for(int i = 1; i <= N; ++ i){
		getint(x);
		addedge(Src, i, x, 0);
		addedge(i + N, Tar, x, 0);
		addedge(Src, i + N, INF, p);
		if(i + m <= N)	addedge(i, i + m + N, INF, f);
		if(i + n <= N)	addedge(i, i + n + N, INF, s);
		if(i < N)	addedge(i, i + 1, INF, 0);
	}
	printf("%d\n", MCMF());
    return 0;
}









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值