題解 P2916 【[USACO08NOV]Cheering up the Cow G】
阿新 • • 發佈:2020-08-01
核心演算法:Kruskal
題目中說:“刪去儘可能多的邊”。就是指保留一棵最小生成樹。
構圖:
我們取一小段來分析一下:
(可以把這個理解為單位元)
按照題目中的意思,走過這一張的花費為:
C[1] + L + C[2] + L + C[1]
即:C[1] + C[1] + 2L + C[2]
走過這一張的花費為:
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; }