BZOJ 2561 浅谈最小生成树最小割

博客探讨了BZOJ 2561问题,通过比较最大生成树与最小生成树的概念,阐述如何利用最小割来解决问题。文章指出,一条边出现在最小生成树上的条件是它比已有的边权值小,并且无法通过更小权值的边连接两点。将问题转化为寻找最小割,即割最少的边使特定两点分隔到不同集合,这在流量为1的情况下可以用网络流快速解决。

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

这里写图片描述
世界真的很大
这道题的方法还是比较神奇
认真想一下说不定还是能想出来,如果能结合起来好好想想
。。。
略有遗憾
但是学会了还是学会了,这是好事嘛

看题先:

description

 给定一个边带正权的连通无向图G=(V,E),其中N=|V|,M=|E|,N个点从1N依次编号,给定三个正整数u,v,和L (u≠v),假设现在加入一条边权为L的边(u,v),那么需要删掉最少多少条边,才能够使得这条边既可能出现在最小生成树上,也可能出现在最大生成树上?

input

第一行包含用空格隔开的两个整数,分别为N和M;
  接下来M行,每行包含三个正整数u,v和w表示图G存在一条边权为w的边(u,v)。
  最后一行包含用空格隔开的三个整数,分别为u,v,和 L;
  数据保证图中没有自环。

output

 输出一行一个整数表示最少需要删掉的边的数量。

最大生成树和最小生成树是差不多的,所以我们先考虑最小生成树的情况
对于这条给出的边,其连接的两点在一颗最小生成树上肯定是联通的,如果要使得这条边出现在最小生成树上,就需要比最小生成树上的边权值小
那我们就把所有权值比这条边小的选出来,如果要删起码会删这些边,删权值比其大的删了也没什么用
如果这些边能够使得u,v两点连通,那么就已经有一颗比这条边小的最小生成树了
这条边会出现在最小生成树上,当且仅当比他小的那些边不能使得u和v连通,这条边才有必要出现
那么题目等价于割最少的边使得u,v分开到两个集合里
给每条边付权值为1的话,就是最小割了
虽然数据范围大了一点,但是网络流据说对于流量为1的边是很快的

最大生成树同理

完整代码:

#include<stdio.h>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

const int INF=0x3f3f3f3f;

struct edge
{
    int u,v,w,last;
}ed[1000010],sid[1000010];

queue <int> state ;

int n,m,num=1,S,T,W,ans=0;
int head[1000010],dis[1000010];

void add(int u,int v,int w)
{
    num++;
    ed[num].v=v;
    ed[num].w=w;
    ed[num].last=head[u];
    head[u]=num;
}

bool bfs()
{
    while(!state.empty()) state.pop();
    memset(dis,-1,sizeof(dis));
    dis[S]=0;
    state.push(S);
    while(!state.empty())
    {
        int u=state.front();
        state.pop();
        for(int i=head[u];i;i=ed[i].last)
        {
            int v=ed[i].v;
            if(dis[v]==-1 && ed[i].w)
            {
                dis[v]=dis[u]+1;
                state.push(v);
            }
        }
    }
    return dis[T]!=-1;
}

int dfs(int u,int low)
{
    if(u==T || low==0) return low;
    int a=0;
    for(int i=head[u];i;i=ed[i].last)
    {
        int v=ed[i].v;
        if(dis[v]==dis[u]+1 && ed[i].w)
        {
            int tmp=dfs(v,min(low,ed[i].w));
            ed[i].w-=tmp,ed[i^1].w+=tmp;
            a+=tmp,low-=tmp;
            if(low==0) return a;
        }
    }
    if(low) dis[u]=-1;
    return a;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&sid[i].u,&sid[i].v,&sid[i].w);
    scanf("%d%d%d",&S,&T,&W);
    for(int i=1;i<=m;i++)
        if(sid[i].w<W)
            add(sid[i].u,sid[i].v,1),add(sid[i].v,sid[i].u,0),
            add(sid[i].v,sid[i].u,1),add(sid[i].u,sid[i].v,0);
    while(bfs())
        ans+=dfs(S,INF);
    num=1;
    memset(head,0,sizeof(head));
    for(int i=1;i<=m;i++)
        if(sid[i].w>W)
            add(sid[i].u,sid[i].v,1),add(sid[i].v,sid[i].u,0),
            add(sid[i].v,sid[i].u,1),add(sid[i].u,sid[i].v,0);
    while(bfs())
        ans+=dfs(S,INF);
    printf("%d\n",ans);
    return 0;
}

嗯,就是这样

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值