[USACO08NOV]Cheering up the Cow Gc++
核心算法:Kruskal算法
题目中说:“删去尽量多的边”。就是指保留一棵最小生成树。3d
构图:code
咱们取一小段来分析一下:blog
(能够把这个理解为单位元)排序
按照题目中的意思,走过这一张的花费为:get
C[1] + L + C[2] + L + C[1]it
即:C[1] + C[1] + 2L + C[2]io
走过这一张的花费为:模板
C[1] + L(1,2) + C[2] + L(2,3) + C[3] + L(2,3) + C[2] + L(1,2) + C[1]
即:C[1] + (C[1] + 2L(1,2) + C[2]) + (C[2] + 2L(2,3) + C[3])
因此,咱们能够推出:新的边的权值,为这条边 起点权值+终点权值+两倍边权
而最后的结果,还要加上一开始起点权值。 (即取点权中的最小值,由于题目中要求最小)
代码以下(细节见注释):
#include <bits/stdc++.h> #define MAXN 1000000 #define INF 0x3f3f3f3f int fat[MAXN],siz[MAXN]; int c[MAXN],n,m,ans=0; struct EDGE{int from,to,val;} e[MAXN]; //邻接矩阵 bool cmp(EDGE x,EDGE y){return x.val<y.val;} //比较器 按照边的权值排序 int Find(int x) {return (fat[x]==x) ? x :fat[x]=Find(fat[x]);} void unionn (int x,int y) { x=Find(x); y=Find(y); if(siz[x]>siz[y]) std::swap(x,y); fat[x]=y; siz[y]+=siz[x]; } //并查集模板 bool kruskal() { int k=0; std::sort(e+1,e+m+1,cmp); for(int i=1;i<=m;++i) { if(k==n-1) break; if(Find(e[i].to)!=Find(e[i].from)) { unionn(e[i].to,e[i].from); ++k; ans+=e[i].val; } } return (k==n-1); } // kruskal模板 int main() { int minn=INF; std::scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) {fat[i]=i; siz[i]=1;} //并查集初始化 for(int i=1;i<=n;++i) { std::scanf("%d",&c[i]); minn=std::min(minn,c[i]); //点权值中取最小值 } for(int i=1;i<=m;++i) { std::scanf("%d%d%d",&e[i].from,&e[i].to,&e[i].val); e[i].val=e[i].val*2+c[e[i].from]+c[e[i].to]; //从新定义权值 } if(kruskal()) std::printf("%d",ans+minn); return 0; }