Luogu P2057 [SHOI2007]善意的投票|最小割

Luogu P2057 [SHOI2007]善意的投票|最小割

重题:

  • [SPOJ1693]Coconuts
  • [JLOI2010]冠军调查

题意:有\(n\)个小朋友投票,只有$0 \ 1 $两种选择。每个人都有自己的主见,为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。求冲突数最小是多少?

题解:

又是一道神奇的网络流题。苦思冥想几天都不会。一看题解恍然大悟系列

考虑最小割,将选\(0\)和选\(1\)的分两边,一边连源,一边连汇。再将朋友间连边(注意,所有朋友都要连,而不是连不同选择的(尝试了一下,貌似只连不同选择的能过?欢迎举反例),应连双向边),做最小割。

根据“最大流=最小割”定理,做最大流即可解决。

这样连边的意义?

每条边的流量代表改变的代价

具体来讲,源、汇的连边意味着与其意愿相反的代价,中间代表协商的代价

那么,割掉就代表,与其意愿相反的代价和协商的代价的最小花费

代码

#include<bits/stdc++.h>
using namespace std;
int cc,to[300000],net[300000],fr[300000],l[300000],fx[300000];
int n,m,u,v,x,f[300000],q[300000];bool vis[300000];
void addedge(int u,int v,int len)
{
	cc++;
	to[cc]=v;net[cc]=fr[u];fr[u]=cc;l[cc]=len;fx[cc]=cc+1;
	cc++;
	to[cc]=u;net[cc]=fr[v];fr[v]=cc;l[cc]=0;fx[cc]=cc-1;
}
bool bfs()
{
	for (int i=1;i<=n+2;i++)
	{
		f[i]=2147483647;vis[i]=false;
	}
	f[n+1]=0;vis[n+1]=true;q[0]=n+1;
	int h=0,t=0;
	while (h<=t)
	{
		for (int i=fr[q[h]];i;i=net[i])
		{
			if (vis[to[i]]||!l[i]) continue;
			f[to[i]]=f[q[h]]+1;q[++t]=to[i];
			vis[to[i]]=true;
			if (to[i]==n+2) return true;
		}
		h++;
	}
	return vis[n+2];
}
int dfs(int x,int y)
{
	if (x==n+2) return y; 
	int now=0;
	for (int i=fr[x];i;i=net[i])
	{
		if (f[to[i]]!=f[x]+1||!l[i]) continue;
		int temp=dfs(to[i],min(y,l[i]));
		if (temp) 
		{
			y-=temp;
			l[i]-=temp;
			l[fx[i]]+=temp;
			now+=temp;
		}
		if (!y) return now;
	}
	return now;
}
int max_flow()
{
	int ans=0;
	while (bfs())
	{
		ans+=dfs(n+1,2147483647);
	}
	return ans;
}
int main()
{
	cin>>n>>m;
	for (int i=1;i<=n;i++)
	{
		cin>>x;
		if (x) addedge(n+1,i,1);else addedge(i,n+2,1);
	}
	for (int i=1;i<=m;i++)
	{
		cin>>u>>v;
		addedge(u,v,1);
		addedge(v,u,1);
	}
	cout<<max_flow();
	return 0;
}

广告:欢迎\(hack\)

Test 1

Test 2

题解参考Solution in Luogu

posted @ 2019-05-07 14:06  fmj_123  阅读(108)  评论(0编辑  收藏  举报