Gym - 101667 E How Many to Be Happy?(最小割)

题目链接

题意:给一个连通无向图,对于图中的每条边,这条边可能在某一颗最小生成树上,如果在称为happy的边,否则称为unhappy的边。对于unhappy的边,总是可以删掉一些边使得它变成happy的边,设h(e)为使e变成happy最少需要删的边数,定义happy边的h值为0,让你求所有边的h值之和。

题解:我们知道要是一个边没有在最小生成树里面,能对他造成影响的只有边权比它小的边(在构成最小生成树的过程中就是优先选边权小的边),所以我们在考虑要让这条边到最小生成树里面,就应该使比它边权小的边不连通,这样要是想选比它边权小的就必定要选它了,所以我们枚举每条边,让边权比它小的边连成一个图,起点和终点就是这条边的两个端点,现在要计算删除最少的边让这个图不连通,这就是最小割了。

PS:这里有个小疑问,为什么在建图的过程中反向边也要有流,望大佬提醒。

#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
#include <unordered_set>

const int mod = 998244353;
const int maxn = 5e5 + 5;
const int inf = 1e9;
const long long onf = 1e18;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI 3.14159265358979323846
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int n, m;
int head[maxn], cur[maxn], tot;
int depth[maxn];
int st, ed;

struct Node {
    int u, v, val;
} a[maxn];
struct node {
    int v, next, flow;
} tree[maxn];

void add_edge(int u, int v, int flow) {
    tree[tot] = node{v, head[u], flow};
    head[u] = tot++;
}

void ins(int u, int v, int flow) {
    add_edge(u, v, flow);
    add_edge(v, u, flow);
}

bool bfs() {
    for (int i = 1; i <= n; i++)
        depth[i] = -1;
    depth[st] = 1;
    queue<int> q;
    q.push(st);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = tree[i].next) {
            int v = tree[i].v, flow = tree[i].flow;
            if (flow && depth[v] == -1) {
                depth[v] = depth[u] + 1;
                q.push(v);
            }
        }
    }
    return depth[ed] != -1;
}

int dfs(int u, int flow) {
    if (u == ed)
        return flow;
    int used = 0;
    for (int i = cur[u]; i != -1; i = tree[i].next) {
        int v = tree[i].v, tempflow = tree[i].flow;
        if (flow && depth[v] == depth[u] + 1) {
            int Min = dfs(v, min(flow, tempflow));
            if (Min) {
                flow -= Min, used += Min;
                tree[i].flow -= Min;
                tree[i ^ 1].flow += Min;
            }
        }
    }
    return used;
}

int Dinic() {
    int sum = 0;
    while (bfs()) {
        for (int i = 1; i <= n; i++)
            cur[i] = head[i];
        sum += dfs(st, inf);
    }
    return sum;
}

void init() {
    for (int i = 1; i <= n; i++)
        head[i] = -1;
    tot = 0;
}

void work() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &a[i].u, &a[i].v, &a[i].val);
    int ans = 0;
    for (int i = 1; i <= m; i++) {
        init();
        for (int j = 1; j <= m; j++) {
            if (a[j].val < a[i].val)
                ins(a[j].u, a[j].v, 1);
        }
        st = a[i].u, ed = a[i].v;
        ans += Dinic();
    }
    printf("%d\n", ans);
}

int main() {
#ifndef ONLINE_JUDGE
    //freopen("1.in", "r", stdin);
#endif
    int t = 1, Case = 1;
    // cin >> t;
    while (t--) {
        //  printf("Case %d: ", Case++);
        work();
    }
    return 0;
}

 


 

©️2020 CSDN 皮肤主题: 创作都市 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值