题目链接:

https://www.luogu.org/problemnew/show/P2057

分析:

由0和1的选择我们直觉的想到0与S一堆,1与T一堆。

但是发现,刚开始的主意并不一定是最终的结果。

于是用源点S表示最终选择0的集合。

汇点T表示最终选择1的集合。

如果一个人P选择了0,那么S−>PS->PS>P连一条流量为1的边,然后P−>TP->TP>T连一条流量为0的边。

反之如果P选择了1,那么S−>PS->PS>P连一条流量为0的边,然后P−>TP->TP>T连一条流量为1的边。

最后把所有朋友对之间连流量为1的双向边(关系相互)

代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define inf 0x7fffffff
using namespace std;
int s,t,ans;
int d[505];
struct edge
{
	int to,val,rev;
	edge(int _to,int _val,int _rev)
	{
		to=_to;
		val=_val;
		rev=_rev;
	}
};
vector<edge>e[505];
void add(int x,int y,int w)
{
	e[x].push_back(edge(y,w,e[y].size()));
	e[y].push_back(edge(x,0,e[x].size()-1));
}
bool bfs()
{
	queue<int>q; 
	memset(d,-1,sizeof(d));
	d[s]=0;
	q.push(s);
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=0;i<e[x].size();i++)
		{
			int y=e[x][i].to;
			if(d[y]==-1&&e[x][i].val)
			{
				q.push(y);
				d[y]=d[x]+1;
			}
		}
	} 
	if(d[t]==-1)return 0;
	return 1;
}
int dfs(int x,int low) 
{
    if(x==t || low==0)
        return low;
    int totflow=0; 
    for(int i=0;i<e[x].size();i++)
    {
        int y=e[x][i].to;
        int rev=e[x][i].rev;
        if(d[y]==d[x]+1 && e[x][i].val)
        {
            int a=dfs(y,min(low,e[x][i].val)); 
            e[x][i].val-=a;
            e[y][rev].val+=a;
            low-=a;
            totflow+=a;
            if(low==0) 
                return totflow;
        }
    }
    if(low!=0) 
        d[x]=-1;
    return totflow;
}

void dinic()
{
	while(bfs())
	{
		//printf("%d\n",ans); 
		ans+=dfs(s,inf); 
	}
}
int main()
{
	int n,m,x,y,o;
	scanf("%d%d",&n,&m);
	s=0;t=500;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&o);
		if(o==0)
		{
			add(s,i,1);//add(i,t,0);
		}
		else
		{
			//add(s,i,0);
			add(i,t,1);
		}
	}
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		add(x,y,1);add(y,x,1);
	}
	dinic();
	printf("%d\n",ans);
	return 0;
}