餐巾计划题面:Luogu1251
经典题啊。然后被HN省选拿去当原题考了???
HNOI传送门:BZOJ1221 Luogu2223
除了输入略有出入以外其他包括题面和思路算法都是一样的,所以放在一起写了= =
(双倍经验啊韩寒会画画后悔画韩红)
首先可以发现这是一个最小费用最大流问题
建图比较麻烦。。。我们把图看成一个餐巾的清洗关系循环图
首先要保证每一天有一定数量的餐巾能够使用,我们设源点s,汇点t
首先是餐巾的购买问题
然后把每一天i拆成没洗过的点和洗过的点,首先从s向每天的洗过的点连边(流INF费p(买一块的钱))
重点来啦!关于餐巾使用后的循环问题
对于每天需要的餐巾,我们从每天的洗过的点向t连边(流a[i](每天需要的块数,下同)费0),然后再从s向每天的没洗过的点连边(流a[i]费0)
这样就形成了餐巾的重复利用循环。。。(可以这么解释吧……也就是说餐巾被用过之后再重新流通到清洗关系循环中)
或者更直观的解释是:餐巾从某一天的洗过的点流到汇点t被使用者使用后这些餐巾被又扔到源点s,然后流通到了这一天的没洗过的点
(应该都明白了吧 = =
然后最后就是关于清洗的事情了
a和b都一样的,我们直接从第i天的没洗过的点向第i+a(b)天的洗过的点连边(流INF费fa(fb))
还有一种情况,(没)洗干净的餐巾可以留到下一天使用(清洗),所以再从第i天的(没)洗过的点向第i+1天的(没)洗过的点连边(流INF费0)
为什么这样是对的呢?
首先因为对于所有的没洗过的点连向洗过的点,只有清洗这种途径(购买是从源点s),所以不存在同一天从没洗过的点向洗过的点连边这种事情,这样可以保证循环过程中连边不出现差错。。。
第二,这张图的最大流是已知的了,即所有n天的需要块数之和,所以在这个基础上跑出来的最小费用就是答案了,而这张图设计之巧妙刚好可以满足这个条件
最后就是跑一遍mcmf的事情了
(听说EK的被卡了???zkw大法好!
附餐巾计划问题的代码
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <ctime>
#include <map>
#include <queue>
#include <cstdlib>
#include <string>
#include <climits>
#include <set>
#include <vector>
using namespace std;
long long ans=0;bool vis[100001];
int n,a,b,f,fa,fb,s,t,dist[100001];
int nedge=-1,p[200001],c[200001],cc[200001],nex[200001],head[200001];
inline void addedge(int x,int y,int z,int zz){
p[++nedge]=y;c[nedge]=z;cc[nedge]=zz;nex[nedge]=head[x];head[x]=nedge;
}
inline bool spfa(int s,int t){
memset(vis,0,sizeof vis);vis[t]=1;
for(int i=s;i<=t;i++)dist[i]=1e9;dist[t]=0;
deque<int>q;q.push_back(t);
while(!q.empty()){
int now=q.front();q.pop_front();
for(int k=head[now];k>-1;k=nex[k])if(c[k^1]&&dist[p[k]]>dist[now]-cc[k]){
dist[p[k]]=dist[now]-cc[k];
if(!vis[p[k]]){
vis[p[k]]=1;
if(!q.empty()&&dist[p[k]]<dist[q.front()])q.push_front(p[k]);
else q.push_back(p[k]);
}
}vis[now]=0;
}
return dist[s]<1e9;
}
inline int dfs(int x,int low){
vis[x]=1;if(x==t)return low;
int a,used=0;
for(int k=head[x];k>-1;k=nex[k])if(!vis[p[k]]&&c[k]&&dist[p[k]]==dist[x]-cc[k]){
a=dfs(p[k],min(c[k],low-used));
if(a)c[k]-=a,c[k^1]+=a,used+=a,ans+=(long long)a*cc[k];
if(used==low)break;
}
return used;
}
inline int costflow(){
int flow=0;
while(spfa(s,t)){
vis[t]=1;
while(vis[t]){memset(vis,0,sizeof vis);flow+=dfs(s,1e9);}
}
return flow;
}
int main()
{
memset(nex,-1,sizeof nex);memset(head,-1,sizeof head);
scanf("%d",&n);s=0;t=2*n+1;
for(int i=1;i<=n;i++){
int x;scanf("%d",&x);
addedge(s,i,x,0);addedge(i,s,0,0);
addedge(i+n,t,x,0);addedge(t,i+n,0,0);
}
scanf("%d%d%d%d%d",&f,&a,&fa,&b,&fb);
for(int i=1;i<=n;i++){
addedge(s,i+n,1e9,f);addedge(i+n,s,0,-f);
if(i<n)addedge(i+n,i+n+1,1e9,0),addedge(i+n+1,i+n,0,0);
if(i+a<=n)addedge(i,i+n+a,1e9,fa),addedge(i+n+a,i,0,-fa);
if(i+b<=n)addedge(i,i+n+b,1e9,fb),addedge(i+n+b,i,0,-fb);
}
costflow();printf("%lld",ans);
return 0;
}