【bzoj1934】[Shoi2007]Vote 善意的投票

本文介绍了一种利用最小割算法解决幼儿园小朋友投票冲突问题的方法。通过构建源点S和终点T,将同意和反对午睡的小朋友进行连接,并考虑好友间的相互影响,最终求得最小冲突数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

幼儿园里有n个小朋友打算通过投票来决定睡不睡午觉。对他们来说,这个问题并不是很重要,于是他们决定发扬谦让精神。虽然每个人都有自己的主见,但是为了照顾一下自己朋友的想法,他们也可以投和自己本来意愿相反的票。我们定义一次投票的冲突数为好朋友之间发生冲突的总数加上和所有和自己本来意愿发生冲突的人数。 我们的问题就是,每位小朋友应该怎样投票,才能使冲突数最小?

Input

第一行只有两个整数n,m,保证有2≤n≤300,1≤m≤n(n-1)/2。其中n代表总人数,m代表好朋友的对数。文件第二行有n个整数,第i个整数代表第i个小朋友的意愿,当它为1时表示同意睡觉,当它为0时表示反对睡觉。接下来文件还有m行,每行有两个整数i,j。表示i,j是一对好朋友,我们保证任何两对i,j不会重复。

Output

只需要输出一个整数,即可能的最小冲突数。

Sample Input

3 3
1 0 0
1 2
1 3
3 2

Sample Output

1

HINT

在第一个例子中,所有小朋友都投赞成票就能得到最优解

【解析】

最小割

    两种意见可以看作源点S和T,我们需要做的是割最少的边使得S和T成为两个不同的集合,解释:割掉的边相当于1次冲突(因为若某边被割走,则显然这条边相连的两个点分别通向了S和T,所以算是一次冲突),当S和T还连通时则必然存在一条路径,这样肯定会有冲突,所以需要使得S和T孤立。

    建图:直接将S连向同意的人,T连向不同意的人,若两人是朋友,则在他们之间连一条双向边(这里有些人不理解:若两个人有冲突,则只需要其中任意一个人改变意见就行了,简单说是让a同意b的意见或者b同意a的意见,所以只需割掉一条边满足一种情况就可以了,但是有两种情况,所以建双向边)。最后就是求最小割了,直接套上最大流的模板就ok了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	int x,y,c,next,other;
}a[210000];int len,last[310];
int st,ed;
void ins(int x,int y,int c)
{
	int k1,k2;
	len++;k1=len;
	a[len].x=x;a[len].y=y;a[len].c=c;
	a[len].next=last[x];last[x]=len;
	
	len++;k2=len;
	a[len].x=y;a[len].y=x;a[len].c=0;
	a[len].next=last[y];last[y]=len;
	
	a[k1].other=k2;
	a[k2].other=k1;
}
int list[310],head,tail,h[310];
bool bt_h()
{
	memset(h,0,sizeof(h));h[st]=1;
	list[1]=st;head=1;tail=2;
	while(head!=tail)
	{
		int x=list[head];
		for(int k=last[x];k;k=a[k].next)
		{
			int y=a[k].y;
			if(a[k].c>0 && h[y]==0)
			{
				h[y]=h[x]+1;
				list[tail++]=y;
				if(tail==ed+1) tail=1;
			}
		}
		head++;
		if(head==ed+1) head=1;
	}
	if(h[ed]>0) return true;
	else return false;
}
int findflow(int x,int f)
{
	if(x==ed) return f;
	int t,s=0;
	for(int k=last[x];k;k=a[k].next)
	{
		int y=a[k].y;
		if(a[k].c>0 && h[y]==h[x]+1 && s<f)
		{
			t=findflow(y,min(a[k].c,f-s));
			s+=t;
			a[k].c-=t;
			a[a[k].other].c+=t;
		}
	}
	if(s==0) h[x]=0;
	return s;
}
int main()
{
	int n,m,x,y;
	scanf("%d%d",&n,&m);
	st=n+1;ed=st+1;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&x);
		if(x==1){ins(st,i,1);}
		else ins(i,ed,1);
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		ins(x,y,1);ins(y,x,1);
	}
	int s=0;
	while(bt_h()==true) s+=findflow(st,999999999);
	printf("%d\n",s);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值