传送门
看到假题,我们就应该想到网络流。
从S向不睡的人连单向边,流量1
从睡的人向T连单向边,流量1
每对朋友之间连双向边,流量1
大力最小割一发就可以了。
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#define inf 0x3f3f3f3f
using namespace std;
struct data{int to,next,val;}e[100005];
int dis[305],q[305],head[305],tot=1,S,T,n,m,x,y,ans;
inline int read(){
int x=0;
char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar());
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
}
inline void add(int x,int y,int u){
e[++tot].to=y;
e[tot].val=u;
e[tot].next=head[x];
head[x]=tot;
}
inline void ins(int x,int y,int u){
add(x,y,u);
add(y,x,0);
}
inline int bfs(){
int h=0,t=1,x;
memset(dis,inf,sizeof(dis));
dis[S]=0;
q[1]=S;
while (h<t){
x=q[++h];
for (int i=head[x];i;i=e[i].next)
if (e[i].val&&dis[e[i].to]==inf){
dis[e[i].to]=dis[x]+1;
q[++t]=e[i].to;
}
}
if (dis[T]==inf) return 0;
return 1;
}
int dinic(int x,int flow){
if (x==T) return flow;
int rest=flow,k;
for (int i=head[x];i;i=e[i].next)
if (dis[x]+1==dis[e[i].to]){
int k=dinic(e[i].to,min(rest,e[i].val));
e[i].val-=k;
e[i^1].val+=k;
rest-=k;
if (!rest) break;
}
if (rest) dis[x]=-1;
return flow-rest;
}
int main(){
n=read();
m=read();
S=n+1;
T=S+1;
for (int i=1;i<=n;i++){
scanf("%d",&x);
if (x) ins(i,T,1); else ins(S,i,1);
}
for (int i=1;i<=m;i++){
x=read();
y=read();
add(x,y,1);
add(y,x,1);
}
while (bfs()) ans+=dinic(n+1,inf);
printf("%d",ans);
}