P2057 [SHOI2007] 善意的投票 / [JLOI2010] 冠军调查

P2057 [SHOI2007] 善意的投票 / [JLOI2010] 冠军调查

看到数据范围一眼网络流:

对于每个人,将其拆成两个点\(x,x'\),对应两种选择。
我们对\(x,x'\) 连一条流量为 \(inf\) 的边,表示这个点不能被割。然后分别连边\(S->x x'->T\) 流量根据点的初始选择而定,但是注意,流量应该分别设置为1和2,而非0和1,不然就不能选择性的割了

然后对于他的朋友,直接交叉连边 \(x->y' y->x'\) 边权都是1。

然后最后把答案减掉 \(n\) 就好了(抵消初始流量设置)

Code:

#include<bits/stdc++.h>
const int N=605;
const int inf=1e9;
using namespace std;
int n,m,e_cnt=1,S,T,ans;
struct Edge{
	int to,nxt,flow;
}e[N*N*2];
int head[N];
void add(int x,int y,int fl)
{
	e[++e_cnt]=Edge{y,head[x],fl};
	head[x]=e_cnt;
	e[++e_cnt]=Edge{x,head[y],0};
	head[y]=e_cnt;
}
queue<int> Q;
int dl[N],dis[N],flow[N],pre[N];
void init()
{
	for(int i=S;i<=T;i++)dis[i]=inf,flow[i]=0;
}
bool spfa(int s,int t)
{
	init();
	Q.push(s);
	dis[s]=0;flow[s]=inf;
	while(!Q.empty())
	{
		int u=Q.front();Q.pop();
		dl[u]=0;
		//cout<<u<<":\n";
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to,fl=e[i].flow;
			//cout<<v<<" "<<"="<<dis[v]<<" "<<dis[u]+1<<" "<<fl<<"\n";
			if(dis[v]>dis[u]+1&&fl)
			{
				flow[v]=min(fl,flow[u]);
				dis[v]=dis[u]+1;
				pre[v]=i;
				if(!dl[v])
				{
					Q.push(v);
					dl[v]=1;
				}
			}
		}
		//cout<<"\n";
	}
	//cout<<flow[t]<<"\n";
	return flow[t];
}
void dinic()
{
	while(spfa(S,T))
	{
		int now=T;
		ans+=flow[T];
		while(now)
		{
			//cout<<"now:"<<now<<"\n";
			int i=pre[now];
			e[i].flow-=flow[T];e[i^1].flow+=flow[T];
			now=e[i^1].to;
		}
	}
}
void work()
{
	cin>>n>>m;
	S=0,T=n+n+1;
	for(int i=1,opt;i<=n;i++)
	{
		scanf("%d",&opt);
		if(opt==0)
		{
			add(S,i,2);	add(i+n,T,1);
		}
		else
		{
			add(S,i,1);	add(i+n,T,2);
		}
		add(i,i+n,inf);
	}
	for(int i=1,x,y;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y+n,1);
		add(y,x+n,1);
	}
	dinic();
	printf("%d",ans-n);
}
int main()
{
	//freopen("vote.in","r",stdin);
	//freopen("vote.out","w",stdout);
	work();
	return 0;
}
posted @ 2024-12-06 12:08  liuboom  阅读(14)  评论(0)    收藏  举报