********************题目略长**********************
题目描述
一个餐厅在相继的N天里,第i天需要Ri块餐巾(i=l,2,…,N)。餐厅可以从三种途径获得餐巾。
(1)购买新的餐巾,每块需p分;
(2)把用过的餐巾送到快洗部,洗一块需m天,费用需f分(f<p)。如m=l时,第一天送到快洗部的餐巾第二天就可以使用了,送慢洗的情况也如此。
(3)把餐巾送到慢洗部,洗一块需n天(n>m),费用需s分(s<f)。
在每天结束时,餐厅必须决定多少块用过的餐巾送到快洗部,多少块送慢洗部。在每天开始时,餐厅必须决定是否购买新餐巾及多少,使洗好的和新购的餐巾之和满足当天的需求量Ri,并使N天总的费用最小
输入输出格式
输入格式:
输入文件共3行,第1行为总天数;第2行为每天所需的餐巾块数;第3行为每块餐巾的新购费用p,快洗所需天数m,快洗所需费用f,慢洗所需天数n,慢洗所需费用s。
输出格式:
输出文件共1行为最小的费用。
输入输出样例
说明
N<=2000
ri<=10000000
p,f,s<=10000
时限4s
*************在网络流24题中算是神奇的一题************
网络流24题中,存在各种只是来让人练写模板熟练度的题。不过这题的建图方式算是一股清流,让人很不好想,但想到了也很不好证明,不过证明出来后真的很好写。
对于送到快洗部、慢洗部、直接购买都很好想,拆点后直接连就行了。也就是把一天拆成上午和下午两个点,上午可以接受洗完的餐巾、买新餐巾,下午可以把餐巾送去洗。也就是说,上午的“流”的意义是干净的餐巾,下午的“流”的意义是脏的餐巾。
让第i天“经过”汇点的流为ni显然是行不通的。可以把“拿去用ni个餐巾”拆成两个动作:上午把ni个干净的餐巾给汇点,下午接收从源点来的ni个脏餐巾。
还要注意的是,餐厅里可以攒干净的餐巾,所以第i天上午要连一条边到第i+1天上午。
以上过程其实可以用有上下界网络流来思考,但并不对劲的人并不想这么做。
#include<iostream> #include<iomanip> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #define ll long long #define maxn 4010 #define maxm 2000010 using namespace std; const ll inf=0x7fffffff; ll fir[maxn],nxt[maxm],v[maxm],fl[maxm],w[maxm],cnt; ll mincost,n,m,p,f,s,N,dis[maxn]; bool vis[maxn]; ll read() { ll x=0,f=1; char ch=getchar(); while(isdigit(ch)==0 && ch!=‘-‘) ch=getchar(); if(ch==‘-‘)f=-1,ch=getchar(); while(isdigit(ch))x=x*10+ch-‘0‘, ch=getchar(); return x*f; } void addedge(ll u1,ll v1,ll fl1,ll w1) { w[cnt]=w1,v[cnt]=v1,fl[cnt]=fl1,nxt[cnt]=fir[u1],fir[u1]=cnt++; w[cnt]=-w1,v[cnt]=u1,fl[cnt]=0,nxt[cnt]=fir[v1],fir[v1]=cnt++; } ll spfa() { memset(dis,-1,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[N*2+1]=0; deque<ll >q; q.push_back(N*2+1); while(!q.empty()) { ll u=q.front();q.pop_front(); for(ll k=fir[u];k!=-1;k=nxt[k]) { ll vv=v[k]; if(fl[k^1]>0) { if(dis[vv]>dis[u]-w[k] || dis[vv]==-1) { dis[vv]=dis[u]-w[k]; if(vis[vv]==0) { if(q.empty()==0&& dis[q.front()]>dis[vv]) q.push_front(vv); else q.push_back(vv); } vis[vv]=1; } } } vis[u]=0; } return dis[0]; } ll getfl(ll u,ll nowflow) { vis[u]=1; if(u==N*2+1)return nowflow; ll tmp,sum=0; for(ll k=fir[u];k!=-1;k=nxt[k]) { if(nowflow<=0)break; ll vv=v[k]; if(vis[vv]==0 && fl[k]>0 && dis[vv]+w[k]==dis[u]&& (tmp=getfl(vv,min(nowflow,fl[k])))>0) { fl[k]-=tmp; fl[k^1]+=tmp; nowflow-=tmp; mincost+=tmp*w[k]; sum+=tmp; } } return sum; } int main() { memset(fir,-1,sizeof(fir)); N=read(); for(ll i=1;i<=N;i++) { ll r=read(); addedge(i,N*2+1,r,0); addedge(0,i+N,r,0); } p=read(),m=read(),f=read(),n=read(),s=read(); for(ll i=1;i<=N;i++) { addedge(0,i,inf,p); if(i+m<=N)addedge(i+N,i+m,inf,f); if(i+n<=N)addedge(i+N,i+n,inf,s); if(i+1<=N)addedge(i,i+1,inf,0); } while(spfa()!=-1) { memset(vis,0,sizeof(vis)); getfl(0,0x7fffffff); } cout<<mincost; return 0; }