P1251 餐巾计划问题
【题意】
【分析】
这是一道很好的的拆点建图的费用流,首先这个餐巾因为要分干净和脏的,所以容易想到拆点的做法
然后呢,这个建图就很神了
我们把每一天拆成两个点,晚上和上午
1.S->晚上 和 上午->T ,建立(ri,0)表示晚上接受ri脏餐巾,白天花费ri干净餐巾,费用均为0
2.i天晚上->i+1天晚上,建立(inf,0)表示晚上的脏餐巾可以留任意多个不洗,费用为0
3.S->上午,建立(inf,p)表示可以直接购买干净的餐巾
4.i天晚上->i+m天上午,建立(inf,f)表示可以送到快洗店
5.i天晚上->i+n天上午,建立(inf,s)表示送到慢洗店
【代码】
#include<bits/stdc++.h> using namespace std; typedef long long ll; const ll inf=0x3f3f3f3f3f3f3f3f; const int maxn=4000+5; int n,head[maxn],s,t; struct edge { int to,nxt; ll v,cost; }e[maxn*20]; int cnt=1; void add(int x,int y,ll z,ll zz) { e[++cnt].nxt=head[x]; e[cnt].to=y; e[cnt].v=z; e[cnt].cost=zz; head[x]=cnt; e[++cnt].nxt=head[y]; e[cnt].to=x; e[cnt].v=0; e[cnt].cost=-zz; head[y]=cnt; } ll dis[maxn]; int vis[maxn],pre[maxn]; bool bfs() { queue <int> q; q.push(s); for(int i=s;i<=t;i++) dis[i]=inf,vis[i]=0; dis[s]=0; vis[s]=1; while(!q.empty()) { int u=q.front(); q.pop(); vis[u]=0; for(int i=head[u];i;i=e[i].nxt) { int to=e[i].to; if(dis[to]>dis[u]+e[i].cost && e[i].v) { dis[to]=dis[u]+e[i].cost; pre[to]=i; if(!vis[to]) q.push(to),vis[to]=1; } } } for(int i=s;i<=t;i++) vis[i]=0; return (dis[t]!=inf); } ll mincost,r[maxn]; void mcmf() { while(bfs()) { ll minn=inf; for(int i=t;i!=s;i=e[pre[i]^1].to) minn=min(minn,e[pre[i]].v); for(int i=t;i!=s;i=e[pre[i]^1].to) { e[pre[i]].v-=minn; e[pre[i]^1].v+=minn; mincost+=minn*e[pre[i]].cost; } } } int used[maxn],unused[maxn]; int main() { // freopen("a.in","r",stdin); // freopen("a.out","w",stdout); scanf("%d",&n); s=1; t=2*n+2; for(int i=1;i<=n;i++) scanf("%lld",&r[i]); ll a,b,c,d,e; scanf("%lld%lld%lld%lld%lld",&a,&b,&c,&d,&e); int tot=1; for(int i=1;i<=n;i++) unused[i]=++tot,used[i]=++tot; for(int i=1;i<=n;i++) { add(s,unused[i],inf,a); // add(unused[i],t,r[i],0); // add(s,used[i],r[i],0); if(used[i+1]<=tot) add(used[i],used[i+1],inf,0); if(unused[i+b]<=tot) add(used[i],unused[i+b],inf,c); if(unused[i+d]<=tot) add(used[i],unused[i+d],inf,e); } mcmf(); printf("%lld",mincost); return 0; }