新建一个虚拟节点后直接跑最小生成树即可,从虚拟节点往每个节点连的边权为每个点建发电站的代价,许多人的考场贪心策略是:先构建原图的最小生成树后找一个花费最小的地方建发电厂。但是这样做不对的地方在于:如果每个地方的点权很小但是一些边的边权十分大,就不如多建几个发电厂核算,因此新建虚拟节点就是考虑到了这种情况,每选一条从实际节点到虚拟节点的边就相当于在该实际节点处盖了一座发电站,直接跑Kruskal即可。
考场代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio> 4 #define N 100000 5 using namespace std; 6 int read() 7 { 8 int x=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){x=x*10+ch-48;ch=getchar();} 11 return x*f; 12 } 13 int weight,n,x,sum,parent[N]; 14 struct node 15 { 16 int u,v,w; 17 }f[N]; 18 bool cmp(node a,node b) 19 { 20 return a.w<b.w; 21 } 22 int find(int x) 23 { 24 if(parent[x]==x)return x; 25 return parent[x]=find(parent[x]); 26 } 27 void Union(int u,int v) 28 { 29 int U=find(u),V=find(v); 30 if(U==V)return; 31 parent[U]=V; 32 } 33 int main() 34 { 35 n=read(); 36 for(int i=1;i<=n;i++) 37 { 38 x=read(); 39 f[++sum].u=0;f[sum].v=i;f[sum].w=x; 40 //u[++sum]=i;v[sum]=0;w[sum]=x; 41 } 42 for(int i=1;i<=n;i++) 43 { 44 for(int j=1;j<=n;j++) 45 { 46 x=read(); 47 if(i<=j)continue; 48 f[++sum].u=i;f[sum].v=j;f[sum].w=x; 49 } 50 } 51 for(int i=0;i<=n;i++)parent[i]=i; 52 sort(f+1,f+1+sum,cmp); 53 int num=0; 54 for(int i=1;i<=sum;i++) 55 { 56 int u=f[i].u,v=f[i].v; 57 if(find(u)!=find(v)) 58 { 59 Union(u,v); 60 weight+=f[i].w; 61 num++; 62 } 63 if(num==n)break; 64 } 65 cout<<weight<<endl; 66 return 0; 67 }