[BZOJ1934/Luogu2057][SHOI2007]Vote 善意的投票 题解

题目链接:

BZOJ1934

Luogu2057

首先,看到求最小冲突以及这(nle 300)的数据范围,很容易联想到最小割。

如何建图?

首先把人分成两份,意见为(1)的与源点连边,容量为(1),否则与汇点连边,容量也设为(1),表示更改自己意见的代价。

接着对每一对朋友之间连一条双向边,容量为(1),表示朋友之间的冲突。

最后求一遍最小割(最大流)即可。

这里用的(Dinic),时间复杂度(O(n^3))

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

int n,m,St,Ed;
int Head[305],Next[200005],To[200005],Val[200005],En=1;
int Dep[305];

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

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

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

int main()
{
	scanf("%d%d",&n,&m);
	St=n+1,Ed=n+2;
	for(int i=1,Sup;i<=n;++i)
	{
		scanf("%d",&Sup);
		if(Sup)Add(St,i);
		else Add(i,Ed);
	}
	for(int x,y;m--;)
	{
		scanf("%d%d",&x,&y);
		Add(x,y),Add(y,x);
	}
	int Maxf=0,Fs;
	while(BFS())
		while((Fs=Dinic(St,1<<30)))
			Maxf+=Fs;
	printf("%d
",Maxf);
	return 0;
}
原文地址:https://www.cnblogs.com/LanrTabe/p/10143402.html