朱刘算法
算法:
1、对于每个点,选择它入度最小的那条边
2、如果形成了最小环,那么就缩环成点,并且更新其他点到环的距离。如果没有环,结束
实现:
1、
a
n
s
=
∑
i
=
1
n
d
i
s
[
i
]
ans=\sum_{i=1}^n dis[i]
ans=∑i=1ndis[i] 首先就选择了 n-1 条边,得到了最小的答案,然后这个答案只是贪心选择的结果,可能选到了环,所以需要修正
练习
P4716 【模板】最小树形图
链接:https://www.luogu.com.cn/problem/P4716
题意:求指定根节点的最小树形图
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=100+5,maxm=1e4+5,inf=2e9;
struct Edge
{
int u,v,w;
}edges[maxm];
int dis[maxn],id[maxn],visit[maxn],pre[maxn];
int zhuliu(int n,int m,int rt)
{
int ans=0;
while(1)
{
for(int i=1;i<=n;++i) dis[i]=inf,id[i]=visit[i]=0;
for(int i=1;i<=m;++i)
{
int u=edges[i].u,v=edges[i].v,w=edges[i].w;
if(u!=v&&w<dis[v]) pre[v]=u,dis[v]=w;
}
for(int i=1;i<=n;++i)
if(i!=rt&&dis[i]==inf) return -1;
int cnt=0;
for(int i=1;i<=n;++i)
{
if(i==rt) continue;
ans+=dis[i];
int v=i;
while(visit[v]!=i&&!id[v]&&v!=rt)
visit[v]=i,v=pre[v];
if(!id[v]&&v!=rt)
{
id[v]=++cnt;
for(int u=pre[v];u!=v;u=pre[u]) id[u]=cnt;
}
}
if(cnt==0) return ans;
for(int i=1;i<=n;++i)
if(!id[i]) id[i]=++cnt;
for(int i=1;i<=m;++i)
{
int u=edges[i].u,v=edges[i].v;
edges[i].u=id[u],edges[i].v=id[v];
if(id[u]!=id[v]) edges[i].w-=dis[v];
//对于环的入边会大于等于0,其他的边就会一直都等于0
}
rt=id[rt],n=cnt;
}
}
int n,m,rt;
int main()
{
scanf("%d%d%d",&n,&m,&rt);
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
edges[i]={u,v,w};
}
printf("%d\n",zhuliu(n,m,rt));
return 0;
}
Ice_cream’s world II POJ - 2121
链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121
题意:求不定根的最小树形图,多解时输出最小的根
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e3+5,maxm=1e4+5,inf=2e9;
struct Edge
{
int u,v,w;
} edges[maxm];
int dis[maxn],id[maxn],visit[maxn],pre[maxn];
int pos;
int zhuliu(int n,int m,int rt)
{
int ans=0;
int Case=0;
while(1)
{
++Case;
for(int i=1; i<=n; ++i) dis[i]=inf,id[i]=visit[i]=0;
for(int i=1; i<=m; ++i)
{
int u=edges[i].u,v=edges[i].v,w=edges[i].w;
if(u!=v&&w<dis[v])
{
pre[v]=u,dis[v]=w;
if(u==rt) pos=i;
}
}
for(int i=1; i<=n; ++i)
if(i!=rt&&dis[i]==inf) return -1;
int cnt=0;
for(int i=1; i<=n; ++i)
{
if(i==rt) continue;
ans+=dis[i];
int v=i;
//id==0表示不属于某个环,并且
while(visit[v]!=i&&!id[v]&&v!=rt)
visit[v]=i,v=pre[v];
if(!id[v]&&v!=rt)
{
id[v]=++cnt;
for(int u=pre[v]; u!=v; u=pre[u]) id[u]=cnt;
}
}
if(cnt==0) return ans;
for(int i=1; i<=n; ++i)
if(!id[i]) id[i]=++cnt;
for(int i=1; i<=m; ++i)
{
int u=edges[i].u,v=edges[i].v;
edges[i].u=id[u],edges[i].v=id[v];
if(id[u]!=id[v]) edges[i].w-=dis[v];
}
rt=id[rt],n=cnt;
}
}
int n,m,rt;
int main()
{
while(~scanf("%d%d",&n,&m))
{
ll sum=0;
for(int i=1; i<=m; ++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
u++,v++;
edges[i]= {u,v,w};
sum+=w;
}
for(int i=1; i<=n; ++i) edges[i+m]= {n+1,i,sum+1};
ll ans=zhuliu(n+1,n+m,n+1);
if(ans==-1||ans>2*sum+1) puts("impossible\n");
else printf("%lld %d\n\n",ans-sum-1,pos-m-1);
}
return 0;
}
/*
3 3
0 1 1
1 2 2
2 0 3
3 3
0 1 2
1 2 2
2 0 2
*/