题意:
(n) 个小朋友投票睡不睡午觉,每个人都有自己的想法,有 (m) 对朋友关系,如果朋友之间投票不一样则为发生冲突,但每个人可以投与自己想法相反的票,但改变想法也算一个冲突,怎样使得冲突最小,输出最小冲突数量。((2leq nleq 300,1leq mleq frac{n(n-1)}2))
由数据范围容易联想到网络流做法,但就是不知道怎么建图。
将所有的冲突连成边,比如 (A) 想睡觉,(B) 不想,那么 (A->B) 表示 (A) 与 (B) 冲突,想要接受这个冲突就要花费 (1) 的代价。
如果割掉所有的冲突那么就相当于考虑完所有的代价,所以建完图后跑个最小割。
具体建图的话:
1.将所有想睡觉的连向源点;
2.将所有不想睡觉的连向汇点;
3.将所有朋友关系连上边,这里注意睡觉的向不睡觉的连单向边,意见相同的要互相连双向边。(然而为了方便起见我们可以都连双向边,不会有影响)
网络流建出来的图一般都很玄学,但我们可以举几个简单的例子来思考每条边的意义:
如上图,(1) 想睡觉,(2) 和 (3) 不想,如果意见都保持不变相当于割掉 (1) 连向 (23) 的边,代价为 (2)。但是如果我们只让 (1) 改变为不想睡觉,相当于割掉 (s) 连向 (1) 的边,代价更小为 (1)。所以,割掉睡觉向不睡觉的边相当于保持冲突,割掉 (s) 连向睡觉的边或不睡觉连向 (t) 的边相当于改变想法。
如上图,如果像刚才那样改变 (1) 的想法,就是割掉了 (s) 连 (1) 的边,但是通过 (4) 和 (5) 同样可以和 (23) 流,因为改变了 (1) 的想法后与 (4) 和 (5) 又产生了新的冲突,我们需要继续割掉 (s) 连向 (4) 和 (5) 的边,就是将 (145) 的想法都改变,显然不如让 (1) 和 (23) 冲突更优。所以,相同想法的点建边是为了保证具有相同的想法,如果有一边改变的话这条边也需要被割掉。
理解的差不多,剩下的就是板子了 (qwq):
#include <bits/stdc++.h>
using namespace std;
const int N=301010;
const int inf=0x3f3f3f3f;
int n,m,s,t,cnt=1;
int a[N];
struct E{ int to,nxt,cap; } e[N];
int head[N],cur[N];
int dep[N],vis[N];
queue <int> q;
inline int read() {
int sum = 0, f = 1; char c = getchar();
while(c<'0' || c>'9') { if(c=='-') f = -1; c = getchar(); }
while(c>='0'&&c<='9') { sum = sum * 10 + c - '0'; c = getchar(); }
return sum * f;
}
inline void add(int u,int v,int w) {
e[++cnt] = (E){ v,head[u],w }; head[u] = cnt;
e[++cnt] = (E){ u,head[v],0 }; head[v] = cnt;
}
bool SPFA() {
memset(dep,0x3f,sizeof(dep));
memset(vis,0,sizeof(vis));
for(int i=s;i<=t;i++) cur[i] = head[i];
int now = dep[1]; dep[s] = 0;
q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop(); vis[u] = 0;
for(int i=head[u]; i; i=e[i].nxt) {
int v = e[i].to;
if(e[i].cap && dep[v] > dep[u]+1) {
dep[v] = dep[u]+1;
if(vis[v]) continue;
q.push(v); vis[v] = 1;
}
}
}
return now != dep[t];
}
int DFS(int u,int flow) {
if(u==t || !flow) return flow;
int dinic = 0, f;
for(int i=cur[u]; i; i=e[i].nxt) {
cur[u] = i;
int v = e[i].to;
if(e[i].cap && dep[v]==dep[u]+1) {
f = DFS( v, min(flow-dinic,e[i].cap) );
if(f) {
dinic += f;
e[i].cap -= f;
e[i^1].cap += f;
if(dinic==flow) break;
}
}
}
return dinic;
}
int koala() {
int dalao = 0;
while( SPFA() )
dalao += DFS(s,inf);
return dalao;
}
int main() {
int x,y;
n = read(); m = read();
s = 0; t = n+1;
for(int i=1;i<=n;i++) {
a[i] = read();
if(a[i]) add(s,i,1); else add(i,t,1);
}
while(m--) {
x = read(); y = read();
add(x,y,1); add(y,x,1);
}
cout<<koala();
return 0;
}