问题转化为最小割
建图
- 同意睡觉的点和
S
S
S部分的源点连边, 同理, 反对睡觉的点和
T
T
T部分的汇点连边, 边权都为1
- 朋友之间连边 因为都可以违背自己的意愿, 所以建双向边且边权为1
- 最后每个人只有一个决定(每个点都只能属于一个部分),为了使得冲突最小,割掉最少的边将图分成2部分,即求最小割
#include <bits/stdc++.h>
using namespace std;
const int maxn = 400;
const int maxm = 1e5+5;
const int inf = 1e8;
struct Edge{
int v,next,c;
}edge[maxm];
int n,m,tot,source,sink;
int head[maxn],dep[maxn],cur[maxn];
void add(int u,int v,int c) {
edge[tot].v=v; edge[tot].c=c;
edge[tot].next=head[u]; head[u]=tot++;
}
void addedge(int u,int v,int c) {
add(u,v,c);
add(v,u,0);
}
bool bfs() {
queue<int> que;
memset(dep,-1,sizeof dep);
dep[source]=0;
que.push(source);
while (!que.empty()) {
int u = que.front(); que.pop();
for (int i=head[u]; i!=-1; i=edge[i].next) {
int to = edge[i].v;
if (edge[i].c>0 && dep[to] == -1) {
dep[to] = dep[u] + 1;
que.push(to);
if (to == sink) return 1;
}
}
}
return dep[sink] != -1;
}
int dfs(int u,int dist) {
if (u == sink)
return dist;
for (int &i=cur[u]; i!=-1; i=edge[i].next) {
int to = edge[i].v;
if (edge[i].c>0 && dep[to]==dep[u]+1) {
int temp = dfs(to,min(dist,edge[i].c));
if (temp > 0) {
edge[i].c -= temp;
edge[i^1].c += temp;
return temp;
}
}
}
return 0;
}
int Dinic() {
int ans = 0;
while (bfs()) {
for (int i=0; i<=sink; ++i)
cur[i] = head[i];
while(1) {
int temp = dfs(source,inf);
if (!temp) break;
ans += temp;
}
}
return ans;
}
int main() {
cin >> n >> m;
memset(head,-1,sizeof head);
source=0; sink = n+1;
for(int i=1; i<=n; ++i) {
int temp; cin >> temp;
if (temp)
addedge(source,i,1);
else
addedge(i , sink,1);
}
while (m--) {
int fri_1,fri_2;
cin >> fri_1 >> fri_2;
add(fri_1,fri_2,1);
add(fri_2,fri_1,1);
}
cout << Dinic() << endl;
return 0;
}