Week6 作业 C - 掌握魔法的东东 Gym - 270437H
题目描述:
东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌水
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
东东为所有的田灌水的最小消耗
思路:
要花最少的代价使图联通,是最小生成树问题。但是主要矛盾在于原点选择哪一个,很显然遍历不现实。
这里学到了一个超级妙的方法:建立一个超级原点,把天上看作是一个点,天上这个点到地上n个点的距离分别是Wi,在这个有n+1个点的图上跑最小生成树即可。
代码:
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 using namespace std; 5 const int MAXN=3e2+5; 6 int n,tot,ans; 7 int fa[MAXN]; 8 struct e 9 { 10 int u,v,w; 11 bool operator<(const e &x) const 12 { 13 return w<x.w; 14 } 15 }edge[MAXN*MAXN]; 16 void init(int n) 17 { 18 for(int i=0;i<=n;i++) 19 fa[i]=i; 20 } 21 int find(int x) 22 { 23 if(fa[x]==x) return fa[x]; 24 fa[x]=find( fa[x] ); 25 return fa[x]; 26 } 27 void unite(int x,int y) 28 { 29 int root1=find(x),root2=find(y); 30 fa[root1]=root2; 31 } 32 int main() 33 { 34 cin>>n; 35 for(int i=1;i<=n;i++) 36 { 37 int t1; scanf("%d",&t1); 38 tot++; 39 edge[tot]={0,i,t1}; 40 } 41 for(int i=1;i<=n;i++) 42 for(int j=1;j<=n;j++) 43 { 44 int t1; scanf("%d",&t1); 45 if(i!=j) 46 tot++,edge[tot]={i,j,t1}; 47 } 48 sort(edge+1,edge+tot+1); 49 init(n); 50 int num=0; 51 for(int i=1;i<=tot;i++) 52 { 53 int u=edge[i].u, 54 v=edge[i].v, 55 w=edge[i].w; 56 if( find(u)==find(v) ) continue; 57 else unite(u,v),ans+=w,num++; 58 if(num==n) break; 59 } 60 cout<<ans<<endl; 61 return 0; 62 }