Luogu P2057 最小割

熟练一下 Luogu P2057

问题转化为最小割

建图
  • 同意睡觉和反对睡觉恰好为2部分,
  1. 同意睡觉的点和 S S S部分的源点连边, 同理, 反对睡觉的点和 T T T部分的汇点连边, 边权都为1
  2. 朋友之间连边 因为都可以违背自己的意愿, 所以建双向边且边权为1
  • 最后每个人只有一个决定(每个点都只能属于一个部分),为了使得冲突最小,割掉最少的边将图分成2部分,即求最小割
#include <bits/stdc++.h>
using namespace std;
const int maxn = 400;
const int maxm = 1e5+5;
const int inf = 1e8;

struct Edge{
    int v,next,c;
}edge[maxm];

int n,m,tot,source,sink;
int head[maxn],dep[maxn],cur[maxn];

void add(int u,int v,int c) {
    edge[tot].v=v; edge[tot].c=c;
    edge[tot].next=head[u]; head[u]=tot++;
}
void addedge(int u,int v,int c) {
    add(u,v,c);
    add(v,u,0);
}

bool bfs() {
    queue<int> que;
    memset(dep,-1,sizeof dep);
    dep[source]=0;
    que.push(source);
    while (!que.empty()) {
        int u = que.front(); que.pop();
        for (int i=head[u]; i!=-1; i=edge[i].next) {
            int to = edge[i].v;
            if (edge[i].c>0 && dep[to] == -1) {
                dep[to] = dep[u] + 1;
                que.push(to);
                if (to == sink) return 1;
            }
        }
    }
    return dep[sink] != -1;
}

int dfs(int u,int dist) {
    if (u == sink)
        return dist;
    for (int &i=cur[u]; i!=-1; i=edge[i].next) {
        int to = edge[i].v;
        if (edge[i].c>0 && dep[to]==dep[u]+1) {
            int temp = dfs(to,min(dist,edge[i].c));
            if (temp > 0) {
                edge[i].c -= temp;
                edge[i^1].c += temp;
                return temp;
            }
        }
    }
    return 0;
}

int Dinic() {
    int ans = 0;
    while (bfs()) {
        for (int i=0; i<=sink; ++i)
            cur[i] = head[i];
        while(1) {
            int temp = dfs(source,inf);
            if (!temp) break;
            ans += temp;
        }
    }
    return ans;
}

int main() {
    cin >> n >> m;
    memset(head,-1,sizeof head);
    source=0; sink = n+1;
    for(int i=1; i<=n; ++i) {
        int temp; cin >> temp;
        if (temp)
            addedge(source,i,1);
        else
            addedge(i , sink,1);
    }
    while (m--) {
        int fri_1,fri_2;
        cin >> fri_1 >> fri_2;
        add(fri_1,fri_2,1);
        add(fri_2,fri_1,1);
        //addedge(fri_1,fri_2,1);
    }
    cout << Dinic() << endl;
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值