指挥网络
树形图的定义:没有环,每个点(除了根节点)的入度都是
朱刘算法的过程见OI-wiki;当没有环的时候,就满足了树形图的定义,于是可以结束;否则的话就将所有环缩点(注意此时由于每个点的入度都是
比如
变成
最终答案就是所有选的边的边权和
证明:如果最开始的选择之后无环,那么肯定是最小树形图(达到了下界);否则的话,先看看两个引理
引理一:对图中任意一个环,至少去掉一条边
这个显然
引理二:存在一个最小树形图,使得每个环只去掉一条边
证:如果对任意一个最小树形图,都存在一个环至少去掉两条边,我们找出这个环,假设这个环是
根据引理二,我们找出原图中所有满足每个环只去掉一条边的树形图,设他们组成的集合是
注意下面的代码,想成是一个基环树(但是根节点没有入边,所以可能有两个连通块)
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e4+10,N=110;
int n,m,r;
struct node
{
int u,v,w;
}e[M];
int in[N],id[N],pre[N];
int vis[N];
int solve(int rt)
{
int ans=0;
while(1)
{
for(int i=1;i<=n;i++) in[i]=1e9;//in[i]表示i的入边边权
for(int i=1;i<=m;i++)
if(e[i].u!=e[i].v&&e[i].w<in[e[i].v])
{
in[e[i].v]=e[i].w;
pre[e[i].v]=e[i].u;//pre[v]表示v的入边的起点
}
for(int i=1;i<=n;i++)
if(i!=rt&&in[i]==1e9) return -1;//如果不连通就无解
int cnt=0;
memset(id,0,sizeof(id));//id[i]表示点i缩点之后的编号
memset(vis,0,sizeof(vis));//vis[i]表示点是否访问过
//vis不能为bool类型
//这是因为如果我们从根所在连通块的某个点出发找环
//是找不到的
//但是如果vis为bool类型
//那么第二次从根所在连通块的某个点出发找环的时候
//就会进入 if(v!=rt&&!id[v]) 这个语句
//这样子就错了
//看不懂可以看洛谷模板题“最小树形图”的第一个样例
in[rt]=0;
for(int i=1;i<=n;i++)
{
ans+=in[i];
int v=i;
while(vis[v]!=i&&!id[v]&&v!=rt)
{
vis[v]=i;
v=pre[v];
}
if(v!=rt&&!id[v])//如果不是根节点所在连通块
{
cnt++;
for(int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
id[v]=cnt;
i++;
for(;i<=n;i++) ans+=in[i];//将剩余边权全部加上
}
}
if(!cnt) return ans;//没有环了
for(int i=1;i<=n;i++) if(!id[i]) id[i]=++cnt;//重新编号
for(int i=1;i<=m;i++)
{
e[i].w-=in[e[i].v];//调整边权
//这里第三类边也要调整,因为我们的ans是累加的
e[i].u=id[e[i].u],e[i].v=id[e[i].v];
}
n=cnt;//调整数量
rt=id[rt];
}
}
int main()
{
scanf("%d%d%d",&n,&m,&r);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
printf("%d",solve(r));
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构