(努力让自己的题解变成认真的题解,但还是忍不住要吐槽,一本通里题目名字为什么都这么中二)
一开始,觉得就是一个最小生成树的裸题。
于是就没想太多,直接,在点权值中取最小值,再直接求最小生成树,求和。
结果凄惨的只拿了50,de了半天bug,甚至怀疑过自己是不是kruskal的板子写错了
还是看了别人的正解,才发现自己思路有漏洞
应该建一个虚拟源点,每个点i连向虚拟源点的边权为发电站费用v[ i ]
为什么呢..?因为虽然是双向边,但存边的时候只能单向存。
这就导致,从哪个点出发,会决定了,有的边加不加的进去。
于是...不能简简单单的取出发电站费用中最少的.
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; inline int read() { int sum = 0,p = 1; char ch = getchar(); while(ch < '0' || ch > '9') { if(ch == '-') p = -1; ch = getchar(); } while(ch >= '0' && ch <= '9') { (sum *= 10) += ch - '0'; ch = getchar(); } return sum * p; } const int N = 310; int n,ans,tot,cnt; struct edge { int frm,to,wei; }e[N * N]; int fa[N]; int findfa(int x) { return fa[x] == x ? x : findfa(fa[x]); } bool cmp(edge a,edge b) { return a.wei < b.wei; } void kruskal() { sort(e+1,e+cnt+1,cmp); for(int i = 0;i <= n;i++) fa[i] = i; for(int i = 1;i <= cnt;i++) { int u = findfa(e[i].frm); int v = findfa(e[i].to); if(u == v) continue; fa[u] = v; ans += e[i].wei; if(++tot == n) break; } } int main() { n = read(); int a; for(int i = 1;i <= n;i++) { a = read(); e[++cnt].frm = 0; e[cnt].to = i; e[cnt].wei = a; } for(int i = 1;i <= n;i++) for(int j = 1;j <= n;j++) { a = read(); if(i == j) continue; e[++cnt].frm = i; e[cnt].to = j; e[cnt].wei = a; } kruskal(); printf("%d",ans); return 0; }