P1251 餐巾计划问题

因为脏的衣服在一天结束时才会有,干净的一天开始才会有,考虑拆点。

再考虑干净的衣服都是要被收集起来的,所以干净的要流向超汇。而脏的衣服可以从超源免费获得所需要的个数,而干净的衣服只能购买。

看张图吧,假如只有 1 个点的话,且需要无限多的衣服。

显然前者正确,后者矛盾(收集了脏衣服)

再者,这种建图会无法收集到当天的脏衣服。

假如从 T 点再回来也不现实,所以只能从 S 供给了。

因为最后一定满流,所以从 S 供给脏衣服是正确的。

#include <bits/stdc++.h>
using namespace std;
int rd() {
    int f=1,sum=0; char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1; ch=getchar();}
    while(isdigit(ch)) {sum=(sum<<3)+(sum<<1)+ch-'0';ch=getchar();}
    return sum*f;
}

#define int long long
#define N (int)(4e4+5)
#define inf (int)(2e18)
struct edge {
	int nex,to,w,c;
}e[N<<1];
int hea[N],cnt=1;
int n,S,T,ans;

void add_edge(int x,int y,int z,int c) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; e[cnt].w=z; e[cnt].c=c; hea[x]=cnt;
}

void add(int x,int y,int z,int c) {
	add_edge(x,y,z,c);
	add_edge(y,x,0,-c);
}

deque<int>q;
bool vis[N];
int dis[N];
bool spfa() {
	for(int i=0;i<N;i++) dis[i]=inf,vis[i]=0;
	q.push_back(S); dis[S]=0;
	while(!q.empty()) {
		int x=q.front(); q.pop_front();
		vis[x]=0;
		for(int i=hea[x];i;i=e[i].nex) {
			int y=e[i].to;
			if(e[i].w&&dis[y]>dis[x]+e[i].c) {
				dis[y]=dis[x]+e[i].c;
				if(!vis[y]) {
					vis[y]=1;
					if(!q.empty()&&dis[y]<dis[q.front()]) q.push_front(y);
					else q.push_back(y);
				}
			}
		}
	}
	return dis[T]<inf;
}

int dfs(int x,int lim) {
	if(x==T||!lim) return lim;
	int flow=0,fl;
	vis[x]=1;
	for(int i=hea[x];i&&lim;i=e[i].nex) {
		int y=e[i].to;
		if(e[i].w&&!vis[y]&&dis[y]==dis[x]+e[i].c) {
			fl=dfs(y,min(lim,e[i].w));
			if(!fl) continue;
			flow+=fl; lim-=fl; e[i].w-=fl; e[i^1].w+=fl;
			ans+=e[i].c*fl;
		}
	}
	if(!flow) dis[x]=inf;
	vis[x]=0;
	return flow;
}

void dinic() {
	while(spfa()) {
		memset(vis,0,sizeof(vis));
		dfs(S,inf);
	}
}
int a[N];
signed main() {
	int x,y;
	n=rd(); S=0; T=2*n+1;
	for(int i=1;i<=n;i++) {
		a[i]=rd();
		add(S,i+n,a[i],0); add(i,i+n,inf,0); add(i,T,a[i],0);
	}
	x=rd();
	for(int i=1;i<=n;i++) add(S,i,inf,x);
	x=rd(); y=rd();
	for(int i=1;i<=n;i++) {
		if(i+x<=n) add(i+n,i+x,inf,y);
	}
	x=rd(); y=rd();
	for(int i=1;i<=n;i++) {
		if(i+x<=n) add(i+n,i+x,inf,y);
	}
	for(int i=1;i<n;i++) add(i+n,i+n+1,inf,0);
	dinic();
	cout<<ans;
	return 0;
}
posted @ 2022-02-08 18:05  FxorG  阅读(23)  评论(0编辑  收藏  举报