洛谷 P2057 [SHOI2007]善意的投票

题目链接

考察网络流建图,从最小割的角度考虑比较容易

改写一下题意:

一张图,每个点初始有0/1两个值中的一个,现在让你重新赋值,变化其中一些点的值(还是0/1),使得新图里 相邻两点值不同的对数 加上 新图里点值与原图不同的点数量 的和最小

相当于我们原来有一张图,里面的点分属两个集合,先改变\(x\)个点,使它们所处集合变化,得到新的集合划分,此时原图有\(y\)条边的端点分属不同集合

求最小\(x+y\)

这样转化一下,题目就有了网络流的雏形

先看\(y\),很明显像最小割,我们只要让两个集合分别包含源点/汇点,端点分属两集合的边就是最小割里割去的边

设流量为1,\(y\)就是最小割

然后看\(x\),假设值为0属于源点集合,值为1属于汇点集合

对于值为0的点,跟源点连容量为1的边,跟汇点连容量为0的边(等于不连边),

这样,我们把该点划分到源点集合,割掉与汇点相连的边,代价是0,反过来代价是1,符合题意

其余同理

这样我们就建好了图,跑网络流即可

[code]


#include <bits/stdc++.h>
using namespace std;

int read(){
    int x=0,flag=1; char c;
    for(c=getchar();!isdigit(c);c=getchar()) if(c=='-') flag=-1;
    for(;isdigit(c);c=getchar()) x=((x+(x<<2))<<1)+(c^48);
    return x*flag;
}

const int N=350;

int S,T,n,m;

struct Edge{
    int to,next,v;
}edge[N*N*2];
int head[N];
int tmp=-1;

void add_edge(int x,int y,int z){
    ++tmp;
    edge[tmp].to=y; edge[tmp].next=head[x]; edge[tmp].v=z;
    head[x]=tmp;
}

void add(int x,int y,int z){
    add_edge(x,y,z); add_edge(y,x,0);
}

int dep[N];
int cur[N];

bool bfs(){
    queue<int> q;
    memset(dep,-1,sizeof(dep));
    q.push(S); dep[S]=0;
    while(!q.empty()){
        int f=q.front(); q.pop();
        for(int i=head[f];i!=-1;i=edge[i].next)
        if(edge[i].v){
            int t=edge[i].to;
            if(dep[t]!=-1) continue;
            dep[t]=dep[f]+1;
            q.push(t);
        }
    }
    return dep[T]!=-1;
}

int dfs(int nod,int val){
    if(!val||nod==T) return val;
    int flow=0;
    for(int i=head[nod];i!=-1;i=edge[i].next)
    if(edge[i].v){
        int t=edge[i].to;
        if(dep[t]!=dep[nod]+1) continue;
        int x=dfs(t,min(val,edge[i].v));
        val-=x; edge[i].v-=x;
        edge[i^1].v+=x; flow+=x;
        if(!val) break;
    }
    return flow;
}

int Dinic(){
    int ret=0;
    while(bfs()){
        memcpy(cur,head,sizeof(head));
        ret+=dfs(S,99999999);
    }
    return ret;
}

int main() {
    memset(head,-1,sizeof(head));
    n=read(),m=read();
    S=0; T=n+1;
    for(int i=1;i<=n;i++) { int x=read(); if(x==0) add(i,T,1); else add(S,i,1); } 
    for(int i=1;i<=m;i++){
        int x=read(),y=read();
        add(x,y,1); add(y,x,1);
    }
    printf("%d\n",Dinic());
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zzhzzh123/p/12208484.html