[BZOJ1497/Luogu4174][NOI2006]最大获利

题目链接:

BZOJ1497

Luogu4174

最大权闭合子图应用。

对于每一个中转站,向汇点连边,容量为费用的绝对值。

对于每一个用户,向源点连边,容量为收益。同时向他需要的中转站连边,容量为(infty)

然后所有用户的收益-上图最小割即为所求答案。

为什么呢?

因为中转站与汇点连边若割断则代表建立中转站(答案减去费用)

若割断用户与源点连边则代表放弃用户(答案减去收益)

那么上网络流即可。

这里写了(Dinic)(忘判细节,(TLE*n)加了当前弧负优化常数x4)和(ISAP)两种方式。

(Dinicquad 6320kbquad 1012ms(BZOJ))

#include <queue>
#include <cstdio>
#include <cstring>

inline int Min(int a,int b){return a<b?a:b;}

int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dep[55555],Cur[55555];

inline void Add(int x,int y,int z)
{
	Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
	Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}

bool BFS()
{
	std::queue<int> q;
	memset(Dep,0,sizeof Dep);
	q.push(St),Dep[St]=1;
	for(int x,y;!q.empty();q.pop())
		for(int i=Head[x=q.front()];i;i=Next[i])
			if(Val[i]&&!Dep[y=To[i]])
			{
				Dep[y]=Dep[x]+1,q.push(y);
				if(y==Ed)return true;
			}
	return false;
}

int Dinic(int x,int Flow)
{
	if(x==Ed)return Flow;
	int Rest=Flow;
	for(int i=Head[x],y;i&&Rest;i=Next[i])
		if(Val[i]&&Dep[y=To[i]]==Dep[x]+1)
		{
			int k=Dinic(y,Min(Val[i],Rest));
			if(!k)Dep[y]=0;
			else Val[i]-=k,Val[i^1]+=k,Rest-=k;
		}
	return Flow-Rest;
}

int main()
{
	scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
	int Ans=0;
	for(int i=1,p;i<=n;++i)
		scanf("%d",&p),Add(i,Ed,p);
	for(int i=1,a,b,c;i<=m;++i)
	{
		scanf("%d%d%d",&a,&b,&c);
		Add(i+n,a,0x3f3f3f3f);
		Add(i+n,b,0x3f3f3f3f);
		Add(St,i+n,c),Ans+=c;
	}
	int MaxFlow=0,Fs;
	while(BFS())
		while((Fs=Dinic(St,0x3f3f3f3f)))
			MaxFlow+=Fs;
	printf("%d
",Ans-MaxFlow);
	return 0;
}

(ISAPquad 6596kbquad 652ms(BZOJ))

#include <queue>
#include <cstdio>
#include <cstring>

inline int Min(int a,int b){return a<b?a:b;}

int n,m,St,Ed;
int Head[55555],Next[400005],To[400005],Val[400005],En=1;
int Dis[55555],Cnt[55555],Cur[55555],Pre[55555];

inline void Add(int x,int y,int z)
{
	Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
	Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}

int Augment()
{
	int Res=0x3f3f3f3f;
	for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
	for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
	return Res;
}

int ISAP()
{
	Cnt[0]=Ed;
	int Flow=0,x=St;
	while(Dis[x]<Ed)
	{
		if(x==Ed)Flow+=Augment(),x=St;
		bool Flag=false;
		for(int i=Cur[x],y;i;i=Next[i])
			if(Val[i]&&Dis[x]==Dis[y=To[i]]+1)
				Flag=true,Pre[y]=Cur[x]=i,x=y,i=0;
		if(Flag)continue;
		int Wd=Ed-1;
		for(int i=Head[x];i;i=Next[i])
			if(Val[i])Wd=Min(Wd,Dis[To[i]]);
		if(!--Cnt[Dis[x]])break;
		++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
		if(x!=St)x=To[Pre[x]^1];
	}
	return Flow;
}

int main()
{
	scanf("%d%d",&n,&m),St=n+m+1,Ed=St+1;
	int Ans=0;
	for(int i=1,p;i<=n;++i)
		scanf("%d",&p),Add(i,Ed,p);
	for(int i=1,a,b,c;i<=m;++i)
	{
		scanf("%d%d%d",&a,&b,&c);
		Add(i+n,a,0x3f3f3f3f);
		Add(i+n,b,0x3f3f3f3f);
		Add(St,i+n,c),Ans+=c;
	}
	int MaxFlow=ISAP();
	printf("%d
",Ans-MaxFlow);
	return 0;
}
原文地址:https://www.cnblogs.com/LanrTabe/p/10168726.html