1 条题解
-
0
自动搬运
来自洛谷,原作者为

D_14134
AFOing。。。搬运于
2025-08-24 21:41:40,当前版本为作者最后更新于2018-10-13 20:37:20,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
最小树形图--朱刘算法
通过不断调整来实现最优解。
大致思路如下:
首先去掉自环。
用贪心的思想给每个点选择一条最小的入边,记录连接的点。
特判此时如果有点没有入边(除根以外),显而易见此时无解。 算上每个点入边的代价,统计答案。
此时判定有没有有向环,如果没有直接输出。 如果有有向环,缩点,并且把连出去的边都减去连出去的点的入边,因为代价已经统计。 二次建图(或多次),对新图重复所有操作。
code
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=100010; const double inf=0x7f7f7f; int n,tot,m,cnt,need[maxn],pre[maxn],vis[maxn],id[maxn]; double ans,cost[maxn],prize,inw[maxn]; struct Edge{ int u,v; double w; }e[maxn]; inline void mst(){ register int num=n,rt=1,idx; while(1){// 初始化 for(register int i=1;i<=num;++i) id[i]=vis[i]=pre[i]=-1,inw[i]=inf;// 选入边 for(register int i=1;i<=cnt;++i) if(inw[e[i].v]>e[i].w&&e[i].u!=e[i].v) inw[e[i].v]=e[i].w,pre[e[i].v]=e[i].u; pre[rt]=rt,idx=inw[rt]=0;// 缩环,统计贡献 for(register int i=1;i<=num;++i){ ans+=inw[i]; if(vis[i]==-1){ register int nw=i; while(vis[nw]==-1) vis[nw]=i,nw=pre[nw]; if(vis[nw]==i&&nw!=rt){ id[nw]=++idx; for(register int j=pre[nw];j!=nw;j=pre[j]) id[j]=idx; } } }// 没有环结束 if(!idx) return; // 重标号,记录新图 for(register int i=1;i<=num;++i) if(id[i]==-1) id[i]=++idx; for(register int i=1;i<=cnt;++i) e[i].w -=inw[e[i].v],e[i].u=id[e[i].u],e[i].v=id[e[i].v]; num=idx,rt=id[rt]; } } int main(){ scanf("%d",&tot),n=2; for(register int i=1;i<=tot;++i){ scanf("%lf%d",&cost[n],&need[n]); if(need[n]) e[++cnt]=(Edge){1,n,cost[n]},vis[i]=n++; } --n,scanf("%d",&m); for(register int i=1,a,b;i<=m;++i){ scanf("%d%d%lf",&a,&b,&prize); a=vis[a],b=vis[b]; if(a && b){ cost[b]=min(cost[b],prize); e[++cnt]=(Edge){a,b,prize}; } } for(register int i=2;i<=n;++i) ans+=(need[i]-1)*cost[i]; mst(); printf("%.2lf\n",ans); return 0; }
- 1
信息
- ID
- 2726
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 6
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者