二者取其一,就是一堆物品,放入两个集合内,放进不同的集合内就会有不同的收益(或代价),使其收益(代价)最大(最小)的一种问题
通常这类问题,使用最小割定理解决。
最小割,即割边集中权值之和最小的一个集合
比如这道题[SHOI2007]善意的投票
是一道这样类型的题。
我们将(S)点设为同意睡觉的超级源点(T)设为不同意睡觉的点,同以对好朋友之间连一条容量为1的无向边,这样建图。我们求得一个最小割,就是答案的解了
不过这里有一个与其他网络流不同的地方,就是朋友间为什么要建立无向边,而不是有向边呢?
我们如此思考,假设a,b不统一意见。有两种决策,
-
让a同意b
-
让b同意a
有两种情况,我们并不能准确确定割边在哪个地方,所以我们需要建出两种情况的边来。
#include<cstdio>
#include<algorithm>
#include<iostream>
#include<queue>
#include<cstring>
using std::queue;
using std::min;
const int maxn=11000;
int n,m;
struct node
{
int p;
long long f;
int nxt;
};
int head[maxn],tail=-1;
int dis[maxn];
int cur[maxn];
node line[maxn<<5];
void add(int a,int b,long long c)
{
line[++tail].p=b;
line[tail].f=c;
line[tail].nxt=head[a];
head[a]=tail;
}
bool bfs(int s,int t)
{
queue<int>q;
memset(dis,0,sizeof(dis));
dis[s]=1;
q.push(s);
while(!q.empty())
{
int pas=q.front();q.pop();
for(int i=head[pas];i!=-1;i=line[i].nxt)
if(!dis[line[i].p]&&line[i].f)
{
dis[line[i].p]=dis[pas]+1;
q.push(line[i].p);
}
}
return dis[t];
}
long long dfs(int now,int aim,long long flow)
{
long long res=0,f;
if(now==aim||!flow) return flow;
for(int &i=cur[now];i!=-1;i=line[i].nxt)
if(dis[line[i].p]==dis[now]+1&&(f=dfs(line[i].p,aim,min(flow,line[i].f))))
{
res+=f;
flow-=f;
line[i].f-=f;
line[i^1].f+=f;
if(!flow) break;
}
return res;
}
long long dinic(int s,int t)
{
long long res=0;
while(bfs(s,t))
{
for(int i=0;i<=n+1;i++)
cur[i]=head[i];
res+=dfs(s,t,0x7fffffff);
}
return res;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
int a,b,c,d;
int ans=0;
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);
ans+=1;
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&a,&b);
add(a,b,1);
add(b,a,1);
}
printf("%d",dinic(0,n+1));
}