题目链接 : https://www.luogu.org/problemnew/show/P1251
题目描述
一个餐厅在相继的 NN 天里,每天需用的餐巾数不尽相同。假设第 ii 天需要 r_iri块餐巾( i=1,2,...,N)。餐厅可以购买新的餐巾,每块餐巾的费用为 pp 分;或者把旧餐巾送到快洗部,洗一块需 m 天,其费用为 f 分;或者送到慢洗部,洗一块需 nn天(n>mn>m),其费用为 ss 分(s<fs<f)。
每天结束时,餐厅必须决定将多少块脏的餐巾送到快洗部,多少块餐巾送到慢洗部,以及多少块保存起来延期送洗。但是每天洗好的餐巾和购买的新餐巾数之和,要满足当天的需求量。
试设计一个算法为餐厅合理地安排好 NN 天中餐巾使用计划,使总的花费最小。编程找出一个最佳餐巾使用计划。
输入输出格式
输入格式:
由标准输入提供输入数据。文件第 1 行有 1 个正整数 NN,代表要安排餐巾使用计划的天数。
接下来的 NN 行是餐厅在相继的 NN 天里,每天需用的餐巾数。
最后一行包含5个正整数p,m,f,n,sp,m,f,n,s。pp 是每块新餐巾的费用; mm 是快洗部洗一块餐巾需用天数; ff 是快洗部洗一块餐巾需要的费用; nn 是慢洗部洗一块餐巾需用天数; ss 是慢洗部洗一块餐巾需要的费用。
输出格式:
将餐厅在相继的 N 天里使用餐巾的最小总花费输出
输入输出样例
输出样例#1:
134
说明
N<=2000
ri<=10000000
p,f,s<=10000
时限4s
刚刚接触的网络流,不是特别的熟练,但网络流的难点还是在于建图,图建出来其他的就是模板套一套,这道题是一道最小费用最大流的题,建图的思想我还没学,直接过来看题,发现根本建不了图(最开始建了一个上下界网络流发现写不了),
直接看了看大佬博客写的非常好,像我这样的菜鸡都看懂了Orz。
首先这道题我们要明确一个事情,一个点可以做两件事(用抹布,洗抹布),这样的话就要分点,如果强行一个点建图的话会有问题(后面跑的时候会给解释),所谓分点就是把一个点分成两个点,分别有不同的意思,
第一个点代表白天,第二个点代表晚上,白天干嘛了呢白天要用抹布,黑天就比较麻烦了,黑天要洗抹布,获得脏抹布,分配如何洗抹布。
那么既然我们要用费用流的话首先要明确,是最小费用最大流还是费用流,如果用费用流的话,流量是多少,用最小费用最大流的话,最大流代表什么,我用的是最小费用最大流,我来说说最大流代表什么,最大流代表白天所需要的抹布。
这么说一定会很懵逼,这个为什么会作为最大流呢,这个就跟我的建图有关如果我把白天用的抹布全部都给了汇点那么当跑到最大流的时候所有的边都会跑满这样就满足了题意而且满足了最大流,那么如果白天的抹布给了汇点,如何把我晚上的脏抹布数增加呢
这个问题我们仔细想想,如果满足题意,白天的话一定会用给定的抹布,而且不会多用(多用多花钱洗,如果不需要洗的话还不如不用),那么我们的当天晚上的脏抹布完全可以知道获得多少,这个知道的话就可以根源点连一个边要这个脏抹布。
还有一个问题就是当晚的脏抹布不一定要全洗剩下的可以留到明天晚上洗。
好了,理论都讲完了,没看懂的话我接下来给建图方式,看懂的话自己可以想一想。
1) 从源点连接每天晚上流量为当天所需的干净抹布数(都变成了脏抹布),费用为0。
2) 从每天白天连接汇点流量为当天所需干净的抹布(当流量最大时,满足每天的抹布数够用),费用为0。
3) 从第i天晚上连接第i+1天晚上流量为INT_MAX,费用为0.(这个是前一天晚上没洗的脏抹布转移给明天)。
4) 从第i天晚上连接第i+k(快洗所需天数)天的 **白天**(是白天,因为洗完变成了干净的抹布可以用了),费用为kq(快洗所需的钱数)。
5) 从第i天晚上连接第i+m(慢洗所需天使)天的 **白天**(同上),费用为mq(慢洗所需钱数)。
6) 从原点连接每天白天流量为INT_MAX,费用为q(买一个抹布所需的钱数)。(如果抹布不够得再买点啊)。
好了都说完了,代码奉上,代码比较丑,大佬轻喷 Orz
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn =1e5+10; #define INT_MAX 0x73f3f3f struct edge{ ll eend; ll weight; ll money; ll next; }x[maxn]; ll head[maxn]; //int ceng[maxn]; ll dian[maxn]; ll bian[maxn]; ll foot[maxn]; ll cnt; ll day[maxn]; queue<ll> q1; void add(ll a,ll b,ll c,ll d){ x[cnt].eend=b; x[cnt].weight=c; x[cnt].money=d; x[cnt].next=head[a]; head[a]=cnt++; x[cnt].eend=a; x[cnt].weight=0; x[cnt].money=-d; x[cnt].next =head[b]; head[b]=cnt++; } int spfa(ll s,ll e){ memset(dian,-1,sizeof(dian)); memset(bian,-1,sizeof(bian)); for(ll i=0;i<maxn;i++) foot[i]=INT_MAX; foot[s]=0; while(q1.size()) q1.pop(); q1.push(s); while(q1.size()){ ll dang=q1.front(); q1.pop(); for(ll i=head[dang];i!=-1;i=x[i].next){ ll to=x[i].eend; if(x[i].weight>0&&foot[to]>foot[dang]+x[i].money){ foot[to]=foot[dang]+x[i].money; dian[to]=dang; bian[to]=i; q1.push(to); } } } if(foot[e]!=INT_MAX){ return 1; } return 0; } int main() { cnt=0; memset(head,-1,sizeof(head)); ll n; ll p,k,kq,m,mq; scanf("%lld",&n); memset(day,0,sizeof(day)); for(ll i=1;i<=n;i++){ scanf("%lld",&day[i]); } scanf("%lld %lld %lld %lld %lld",&p,&k,&kq,&m,&mq); for(ll i=1;i<=n;i++){ add(0,n+i,day[i],0); add(i,2*n+1,day[i],0); add(0,i,INT_MAX,p); if(i<n) add(i+n,i+1+n,INT_MAX,0); if(i+k<=n) add(i+n,i+k,INT_MAX,kq); if(i+m<=n) add(i+n,i+m,INT_MAX,mq); } ll sum=0; while(spfa(0,2*n+1)){ //printf("%d ",foot[2*n+1]); //printf("1 "); ll minn=INT_MAX; for(ll i=2*n+1;i!=0;i=dian[i]){ ll to=bian[i]; minn=min(minn,x[to].weight); } sum+=minn*foot[2*n+1]; for(ll i=2*n+1;i!=0;i=dian[i]){ ll to=bian[i]; x[to].weight-=minn; x[to^1].weight+=minn; } } printf("%lld ",sum); return 0; }