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 }

 

posted @ 2020-04-02 15:37  菜鸡今天学习了吗  阅读(132)  评论(0编辑  收藏  举报