【dinic求最小割】班长竞选

【dinic求最小割】班长竞选_第1张图片【dinic求最小割】班长竞选_第2张图片


大致思路

题目是需要求两个“阵营”之间有多少对好朋友,以及改变了意见的人(其实也是处于两个“阵营”的意思)。

求最小割!(最小割:一个无向连通网络,去掉一个边集可以使其变成两个连通分量则这个边集就是割集;最小割集当然就权和最小的割集。

最小割最大流定理——用dinic求最大流,则其数值=最小割。

注意求最小割的前提是“无向”!所以我们在建图连边的时候都连双向边即可!(即反向边的权值为1而非0)


AC代码

#include
#include
#include
#define maxn 100000
using namespace std;
struct Edge{
    int v,c,next;
    Edge(int v,int c,int next):v(v),c(c),next(next){}
    Edge(){}
};
Edge e[maxn];
int cnt,n,m;
int p[310];
int d[310];
void init(){
    memset(p,-1,sizeof(p));
    cnt=0;
}
void insert(int u,int v,int c){
    e[cnt]=Edge(v,c,p[u]);
    p[u]=cnt++;
}
bool bfs(){
    memset(d,-1,sizeof(d));
    queueq;
    d[0]=0;
    q.push(0);
    while(!q.empty()){
        int u=q.front();q.pop();
        for(int i=p[u];i!=-1;i=e[i].next){
            int v=e[i].v;
            if(e[i].c>0&&d[v]==-1){
                d[v]=d[u]+1;
                q.push(v);
            }
        }
    }
    return d[n+1]!=-1;
}
int dfs(int u,int flow){
    if(u==n+1)return flow;
    int res=0;
    for(int i=p[u];i!=-1;i=e[i].next){
        int v=e[i].v;
        if(e[i].c>0&&d[v]==d[u]+1){
            int tmp=dfs(v,min(flow,e[i].c));
            e[i].c-=tmp;
            flow-=tmp;
            e[i^1].c+=tmp;
            res+=tmp;
            if(flow==0)
                break;
        }
    }
    if(res==0)
        d[u]=-1;
    return res;
}

int main(){
    int k,kk;
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        if(!k){
            insert(0,i,1);
            insert(i,0,1);
        }else{
            insert(i,n+1,1);
            insert(n+1,i,1);
        }
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&k,&kk);
        insert(k,kk,1);
        insert(kk,k,1);
    }
  //  printf("%d\n",cnt);
    int res=0;
    while(bfs()){
       // printf("d[n+1]=%d\n",d[n+1]);
        res+=dfs(0,0x3f3f3f3f);
    }
    printf("%d\n",res);
    return 0;
}




你可能感兴趣的:(蓝桥杯)