题目:餐巾计划问题
思路:
拆点,每天拆成收到干净餐巾和送出脏餐巾两部分。
把源点和送餐巾的点连边,收到餐巾的点和汇点连边,皆是流量w,费用f。
把每一天收到餐巾和送出餐巾年连边,流量inf,费用0。
再按照洗和买的条件连边。
跑费用流即可。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 5000
#define maxm 1000000
#define read(x) scanf("%d",&x)
#define ll long long
#define inf (1<<30)
struct Edge{
int u,v,w;
ll f;
Edge(){}
Edge(int uu,int vv,int ww,ll ff) {u=uu,v=vv,w=ww,f=ff;}
};
int n;
Edge e[maxm+5];
int h[maxn+5],nxt[maxm+5],cnt=-1;
int p,t1,p1,t2,p2;
void add(int u,int v,int w,ll f) {
e[++cnt]=Edge(u,v,w,f);
nxt[cnt]=h[u];h[u]=cnt;
}
void readin() {
memset(h,-1,sizeof(h));
memset(nxt,-1,sizeof(nxt));
read(n);
for(int i=1;i<=n;i++) {
int x;read(x);
add(0,i+n,x,0),add(i+n,0,0,0);
add(i,2*n+1,x,0),add(2*n+1,0,0,0);
}
read(p),read(t1),read(p1),read(t2),read(p2);
for(int i=1;i<=n;i++) {
add(0,i,inf,p),add(i,0,0,-p);
if(i+1<=n)add(i+n,i+n+1,inf,0),add(i+n+1,i+n,0,0);
if(i+t1<=n) add(i+n,i+t1,inf,p1),add(i+t1,i+n,0,-p1);
if(i+t2<=n) add(i+n,i+t2,inf,p2),add(i+t2,i+n,0,-p2);
}
}
ll dist[maxn+5];
int pre[maxn+5];
int vis[maxn+5],maxw[maxn+5];
queue<int> que;
bool spfa() {
for(int i=1;i<=2*n+1;i++) dist[i]=inf;
memset(vis,0,sizeof(vis));
que.push(0);maxw[0]=inf;
while(!que.empty()) {
int x=que.front();que.pop();
vis[x]=false;
for(int i=h[x];~i;i=nxt[i]) {
Edge y=e[i];
if(y.w>0&&dist[y.v]>dist[y.u]+y.f) {
dist[y.v]=dist[y.u]+y.f;
pre[y.v]=i;
maxw[y.v]=min(maxw[y.u],y.w);
if(!vis[y.v]) que.push(y.v),vis[y.v]=true;
}
}
}
if(dist[2*n+1]!=inf) return true;
return false ;
}
void update(){
int x=n*2+1;
while(x!=0) {
e[pre[x]].w-=maxw[n*2+1];
e[pre[x]^1].w+=maxw[n*2+1];
x=e[pre[x]].u;
}
}
ll dinic() {
ll ans=0;
while(spfa()) {update();ans+=maxw[2*n+1]*dist[2*n+1];}
return ans;
}
int main() {
readin();
ll ans=dinic();
printf("%lld",ans);
return 0;
}