掌握魔法の东东 I

题解:

东东在老家农村无聊,想种田。农田有 n 块,编号从 1~n。种田要灌氵
众所周知东东是一个魔法师,他可以消耗一定的 MP 在一块田上施展魔法,使得黄河之水天上来。他也可以消耗一定的 MP 在两块田的渠上建立传送门,使得这块田引用那块有水的田的水。 (1<=n<=3e2)
黄河之水天上来的消耗是 Wi,i 是农田编号 (1<=Wi<=1e5)
建立传送门的消耗是 Pij,i、j 是农田编号 (1<= Pij <=1e5, Pij = Pji, Pii =0)
东东为所有的田灌氵的最小消耗

Input

第1行:一个数n
第2行到第n+1行:数wi
第n+2行到第2n+1行:矩阵即pij矩阵

Output

东东最小消耗的MP值

Example

 

Input
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
Output
9

我的题解:

如果忽略天上水,则为最小生成树为题,那么,我们怎么解决这添上水呢?

事实上,如果把它当成一个超级原点(连接所有其他点)的话,问题就转化成了我们熟悉的最小生成树问题!

最小生成树我采用kruskal的方法,每次从中选取(合法的)最小边。

判断成环与否我们用并查集思想:两端点如果不属于一个集合,那么证明添加后不会成环。

 1 #include<iostream>
 2 #include<algorithm>
 3 using namespace std;
 4 int par[310], rnk[310],nums=0;
 5 typedef struct node{
 6     int u,v,w;    
 7     bool operator<(node &edg){
 8         return w<edg.w;
 9     }
10 }Edge;
11 
12 Edge Edges[100000];
13 
14 int find(int x) 
15 { return par[x]==x? x : par[x]=find(par[x]);}
16 
17 bool unite(int x,int y){
18     x=find(x);y=find(y);
19     if(x==y) return false;
20     if(rnk[x]>rnk[y]) swap(x,y);
21     par[x]=y; rnk[y] += rnk[x]; 
22     return true;
23 } 
24 
25 int main(){
26     int n;cin>>n;  
27     for(int i=0;i<n+1;i++){
28      par[i]=i; 
29      rnk[i]=1;
30     } 
31     int w;
32     for(int i=0;i<n;i++){
33         cin>>w;
34         Edges[nums].u=0; 
35         Edges[nums].v=i+1;
36         Edges[nums++].w=w;
37     }
38     for(int i=0;i<n;i++){
39         for(int j=0;j<n;j++){
40             cin>>w;
41             if(i!=j)
42             {
43              Edges[nums].u=i+1; 
44              Edges[nums].v=j+1;
45              Edges[nums++].w=w;
46              }
47         }
48     }    
49     sort(Edges,Edges+nums);    
50     int sum=0,cnt=0;
51     for(int i=0;i<nums;i++){
52       if(unite(Edges[i].u,Edges[i].v)){
53           sum+=Edges[i].w;
54           cnt++;
55       }    
56       if(cnt==n) break;    
57     }
58     cout<<sum<<endl;
59     return 0;
60 }

 

posted @ 2020-04-03 09:55  流转~星云  阅读(143)  评论(0编辑  收藏  举报