bzoj 1937 最小生成树【费用流】

本文介绍了一种利用最小费用流算法解决特定图论问题的方法:即给定一张无向图和一棵生成树,如何计算使得这棵树变为最小生成树所需的最小代价。文章通过构造特殊的网络流模型,实现了这一目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目大意:给定一张无向图和一颗生成树,求使这棵树变成最小生成树的最小代价

我们知道最优情况下如果要改变,树上边只会减小,非树边只会增大。对于每条非树边 j 两端点间的树边 i,就有 wj+dj>=widi,就是 di+dj>=wiwj

那么建图就是:
· S -> i (1,0)
· i -> j (1,wiwj)
· j -> T (1,0)

然后跑最大费用流就好啦QAQ

#include<iostream>
#include<cstring>
#include<cstdio>
#define N 805
#define M 100000
#define INF 1000000000
using namespace std;

int n,m,siz = 1,S,T;
int a[55][55],b[55][55],id[55][55],fa[55],x[N],y[N];
int first[N],next[M],to[M],flow[M],cost[M];
int d[N],p[M * 10],pre[N];
bool v[N];

void inser(int x,int y,int w,int c)
{
    next[++ siz] = first[x];
    first[x] = siz;
    to[siz] = y;
    flow[siz] = w;
    cost[siz] = c;
}

void add_edge(int x,int y,int w,int c)
{
    inser(x,y,w,c),inser(y,x,0,-c);
}

bool spfa()
{
    int head = 0,tail = 1;
    memset(d,192,sizeof(d));
    memset(pre,0,sizeof(pre));
    d[p[1] = S] = 0;
    while (head ^ tail)
    {
        int x = p[++ head];
        v[x] = false;
        for (int i = first[x];i;i = next[i])
            if (flow[i] && d[to[i]] < d[x] + cost[i])
            {
                d[to[i]] = d[x] + cost[i];
                pre[to[i]] = i;
                if (!v[to[i]]) v[p[++ tail] = to[i]] = true;
            }
    }
    return d[T] ^ d[0];
}

int mcmf()
{
    int ret = 0;
    while (spfa() && d[T] > 0)
    {
        int w = INF;
        for (int i = pre[T];i;i = pre[to[i ^ 1]])
            w = min(w,flow[i]);
        for (int i = pre[T];i;i = pre[to[i ^ 1]])
            flow[i] -= w,flow[i ^ 1] += w;
        ret += d[T] * w;
    }
    return ret;
}

void dfs(int x)
{
    for (int y = 1;y <= n;y ++)
        if (b[x][y] && y != fa[x])
            fa[y] = x,dfs(y);
}

int main()
{
    scanf("%d%d",&n,&m);
    for (int i = 1;i <= m;i ++)
    {
        scanf("%d%d",&x[i],&y[i]);scanf("%d",&a[x[i]][y[i]]);
        a[y[i]][x[i]] = a[x[i]][y[i]];
        id[x[i]][y[i]] = id[y[i]][x[i]] = i;
    }
    for (int u,v,i = 1;i < n;i ++)
    {
        scanf("%d%d",&u,&v);
        b[u][v] = b[v][u] = a[u][v];
        a[u][v] = a[v][u] = 0;
    }
    for (int i = 1;i <= m;i ++) if (a[x[i]][y[i]])
    {
        int u = x[i],v = y[i],w = a[u][v];
        fa[v] = 0,dfs(v);
        for (int z = u;z ^ v;z = fa[z])
            if (b[z][fa[z]] > w) add_edge(id[z][fa[z]],i,1,b[z][fa[z]] - w);
    }
    S = m+1,T = S+1;
    for (int i = 1;i <= m;i ++)
        b[x[i]][y[i]] ? add_edge(S,i,1,0) : add_edge(i,T,1,0);
    cout << mcmf() << endl;

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值