题目链接:
首先,看到求最小冲突以及这\(n\le 300\)的数据范围,很容易联想到最小割。
如何建图?
首先把人分成两份,意见为\(1\)的与源点连边,容量为\(1\),否则与汇点连边,容量也设为\(1\),表示更改自己意见的代价。
接着对每一对朋友之间连一条双向边,容量为\(1\),表示朋友之间的冲突。
最后求一遍最小割(最大流)即可。
这里用的\(Dinic\),时间复杂度\(O(n^3)\)
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
int n,m,St,Ed;
int Head[305],Next[200005],To[200005],Val[200005],En=1;
int Dep[305];
inline void Add(int x,int y)
{
Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=1;
Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}
bool BFS()
{
std::queue<int> q;
memset(Dep,0,sizeof Dep);
Dep[St]=1,q.push(St);
while(!q.empty())
{
int x=q.front(),y;
q.pop();
for(int i=Head[x];i;i=Next[i])
if(Val[i]&&!Dep[y=To[i]])
{
Dep[y]=Dep[x]+1;
if(y==Ed)return true;
q.push(y);
}
}
return false;
}
int Dinic(int x,int Flow)
{
if(x==Ed)return Flow;
int k,Rest=Flow,y;
for(int i=Head[x];i&&Rest;i=Next[i])
if(Val[i]&&Dep[y=To[i]]==Dep[x]+1)
{
k=Dinic(y,std::min(Rest,Val[i]));
if(!k)Dep[y]=0;
Val[i]-=k,Val[i^1]+=k,Rest-=k;
}
return Flow-Rest;
}
int main()
{
scanf("%d%d",&n,&m);
St=n+1,Ed=n+2;
for(int i=1,Sup;i<=n;++i)
{
scanf("%d",&Sup);
if(Sup)Add(St,i);
else Add(i,Ed);
}
for(int x,y;m--;)
{
scanf("%d%d",&x,&y);
Add(x,y),Add(y,x);
}
int Maxf=0,Fs;
while(BFS())
while((Fs=Dinic(St,1<<30)))
Maxf+=Fs;
printf("%d\n",Maxf);
return 0;
}