根本读不懂题,大家自己理解一下吧
分析:
总共只有两种状态,对于一个人来说,要么是投1要么就投0.
所以最终可以把状态划分为两个集合,全1和全0
那么问题就好办了。
我们把全1的集合连在源点S,全0的集合连在汇点T
好朋友连双向边。
这样构成的图中,我们求一个割,就可以把图划分为两个部分,由于改变意愿要加一答案,所以边权就是1,然后求最小割,就是最小的答案了。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
const int INF = 1e9+7;
int n,m;
struct edge
{
int to;
int cap;
int rev;
};
vector<edge> G[maxn];
int depth[maxn],arc[maxn];
void add_edge(int from,int to,int cost)
{
G[from].push_back((edge){to,cost,G[to].size()});
G[to].push_back((edge){from,0,G[from].size()-1});
}
void bfs(int s)
{
queue<int> q;
memset(depth,-1,sizeof(depth));
depth[s] = 0;
q.push(s);
while(!q.empty())
{
int v = q.front();
q.pop();
for(int i=0;i<G[v].size();i++)
{
edge &e = G[v][i];
if(e.cap>0 && depth[e.to]<0)
{
depth[e.to] = depth[v]+1;
q.push(e.to);
}
}
}
}
int dfs(int v,int t,int f)
{
if(v==t) return f;
for(int &i=arc[v];i<G[v].size();i++)
{
edge &e = G[v][i];
if(e.cap>0 && depth[e.to]==depth[v]+1)
{
int d = dfs(e.to,t,min(f,e.cap));
if(d>0)
{
e.cap -= d;
G[e.to][e.rev].cap += d;
return d;
}
}
}
return 0;
}
int dinic(int s,int t)
{
int flow = 0;
while(true)
{
bfs(s);
if(depth[t]<0) return flow;
memset(arc,0,sizeof(arc));
int f;
while((f=dfs(s,t,INF))>0)
{
flow += f;
}
}
}
int main()
{
scanf("%d%d",&n,&m);
int s = 0,t = n+1;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(x) add_edge(s,i,1);
else add_edge(i,t,1);
}
for(int i=0;i<m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add_edge(x,y,1);
add_edge(y,x,1);
}
printf("%d\n",dinic(s,t));
return 0;
}