题目大意:有 n 个人在为睡觉而投票,m 对朋友。睡的话他们要一起睡,不睡就都不睡。每个人会有自己的意愿,睡 or 不睡,但是出于照顾朋友的想法,投票的时候可能不会遵循自己的意愿。定义一次投票的冲突数为好朋友之间发生冲突的对数加上和所有和自己本来意愿发生冲突的人数。求最小冲突数。
分析:
假定睡觉是 1 ,不睡觉是 0 。
1、每个人最后都只有两种可能,投了 1 或 0 。
2、将投了 1 的与起点 S 建有向边 S -> i ,投了 0 的与终点 T 建有向边 i -> T,朋友之间也连接一条有向边(方向下面说)。
3、此图求最小割即为答案。
原因:
①、对于一个图的 S -> T 的割来讲,最后会将图分为两块,一块与 S 相连,一块与 T 相连。在本题中就相当于最后每个人的投票结果。
②、若割中包括 S -> i 这条边,相当于第 i 个人他的意愿是 1 而最后他投了 0 ,说明他改变了意愿,对答案做成贡献 1 份,对于割边 i -> T 同理。
③、若割中包含 i -> j 这条边,相当于第 i 个人与 第 j 个人是好友关系且具有相反的投票结果(因为割让他们分到 S T 两边),对答案做出贡献 1 份。
综上:此题应该要求的是建图后的最小割。
如果按上述建图,对于本题无法判断 i -> j 的方向(可想而知,根据网络流的思想,反向边仍然有用处)。故在建边的时候,两个方向边的容量大小都需要设成 1 (而不是反向边设成 0 ,方向不能判断,哪条边都可以是反向边。且由于题意,i -> j 与 j -> i 并不影响,再加上这里反向边设为 1 并不影响跑网络流,因为反向边只是起到了减流增流的效果)
代码如下:
#include<iostream> #include<algorithm> #include<cstring> #include<queue> #define maxn 308 #define inf 0x3f3f3f3f using namespace std; int n,m,cnt,S,T; int head[maxn],d[maxn],cur[maxn]; queue<int> q; struct Edge{ int to; int val; int next; }edge[maxn*maxn*10]; inline void add(int u,int v,int w){ edge[++cnt].to=v; edge[cnt].val=w; edge[cnt].next=head[u]; head[u]=cnt; return; } void init(){ cnt=-1; memset(head,-1,sizeof(head)); return; } bool bfs(){ while(!q.empty()) q.pop(); memset(d,-1,sizeof(d)); d[S]=0; q.push(S); while(!q.empty()){ int u=q.front(); q.pop(); for(int i=head[u];~i;i=edge[i].next){ int v=edge[i].to; if(d[v]==-1&&edge[i].val>0){ d[v]=d[u]+1; q.push(v); } } } return d[T]!=-1; } int dfs(int u,int flow){ int nowflow=0; if(u==T) return flow; for(int i=cur[u];~i;i=edge[i].next){ cur[u]=i; int v=edge[i].to; if(d[v]==d[u]+1&&edge[i].val>0){ if(int k=dfs(v,min(flow-nowflow,edge[i].val))){ edge[i].val-=k; edge[i^1].val+=k; nowflow+=k; if(nowflow==flow) break; } } } if(!nowflow) d[u]=-1; return nowflow; } int Dinic(){ int ans=0; while(bfs()){ for(int i=0;i<=n+1;i++) cur[i]=head[i]; ans+=dfs(S,inf); } return ans; } int main() { scanf("%d%d",&n,&m); init(); int A,B; for(int i=1;i<=n;i++){ scanf("%d",&A); if(A) add(0,i,1),add(i,0,0); else add(i,n+1,1),add(n+1,i,0); } for(int i=1;i<=m;i++){ scanf("%d%d",&A,&B); add(A,B,1),add(B,A,1); } S=0,T=n+1; int ans=Dinic(); printf("%d\n",ans ); }