Fork me on GitHub

HDU1875-畅通工程再续

邝斌专题

邝斌带你飞KMP专题

再切一个最小生成树吧

这个题来自于2008浙大研究生复试热身赛(2)—— 全真模拟,好奇怪,复试居然还有热身赛

HDU1875

就是正常走最小生成树,多两个判断:

0、边是否可以加入进来

1、加进来的边数是否是n-1条(换句话说,之前最小生成树都是默认是连通图,而现在有了dis的规定,10和100范围,导致都不一定连通,所以要判断下 )

 

这题朴素普里姆n²,即T*C*C,10^6

堆优化普里姆mlogn,即

边数m:[1 +(C-1)]*(C-1)/2,依旧是10^6

克鲁斯卡尔一样

先用刚回顾熟悉的堆优化普里姆

 

吓死我了,GPT说“😩抱歉,出了点小问题,请稍后重试 ~~”,我以为我对话的时候骂GPT被他们封了(上一个博客里有图)

 

一次AC调试都没调试,写完运行发现过了样例直接就提交的

AC代码 —— 优先队列 堆优化 普里姆 写法

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<math.h>
 5 #include<queue>
 6 #include<vector>
 7 #include<iomanip>
 8 using namespace std;
 9 #define MAX 102
10 int T;
11 int C;
12 
13 struct Node_date{
14     int x;
15     int y;
16 }map[MAX];
17 
18 struct Edge{
19     int from;
20     int to;
21     double dis;//看似是两点之间距离,
22 //其实主要是染色点距离未染色点的最短距离
23 //比如G[3].dis表示染色的点里,距离3这个点的最近的距离
24 }edge;
25 
26 bool operator<(Edge a, Edge b){
27     return a.dis>b.dis;
28 }
29 vector<Edge>G[MAX];
30 int main()
31 {
32     cin>>T;
33     while(T--){
34         memset(G,0,sizeof(G));
35         cin>>C;
36         for(int i=1;i<=C;i++)
37             cin>>map[i].x>>map[i].y;
38 
39         for(int i=1;i<=C;i++)
40             for(int j=1;j<=C;j++){
41                 if(i==j)
42                     continue;
43                 double dis;
44                 dis=sqrt(pow((map[i].x-map[j].x),2)+pow((map[i].y-map[j].y),2));
45                 if(dis<10 || dis>1000)
46                     continue;
47                 edge.from=i;
48                 edge.to=j;
49                 edge.dis=dis;
50                 G[i].push_back(edge);
51             }
52         //处理数据结束,每个边都是符合条件的
53 
54         //开始普里姆
55         int num_edge=0;
56         double ans=0;
57         priority_queue<Edge>q;
58         while(!q.empty())
59             q.pop();
60         int is_in[MAX];
61         memset(is_in,0,sizeof(is_in));
62         for(int i=0;i<G[1].size();i++)
63             q.push(G[1][i]);
64         is_in[1]=1;
65         while(!q.empty()){
66             edge=q.top();
67             q.pop();
68             if(is_in[edge.to]==1 && is_in[edge.from]==1)
69                 continue;
70 
71             num_edge++;
72             ans+=edge.dis;
73 
74             is_in[edge.to]=1;
75             is_in[edge.from]=1;
76 
77             for(int i=0;i<G[edge.to].size();i++){
78                 q.push(G[edge.to][i]);
79 
80             }
81         }
82         ans=ans*100;
83         if(num_edge==C-1)
84             cout<<fixed<<setprecision(1)<<ans<<endl;//GPT告诉我的
85         else
86             cout<<"oh!"<<endl;
87 
88     }
89 }

 

回顾下:

0、GPT查了下怎么输出一位。考试估计只能用 printf("%.1f\n", ans); 

1、没调试写完直接就可以运行,非常完美

2、但时间太久了,20min,,肛门好疼,冻的痔疮还是不舍得花钱吃东西饿的,导致营养不良的直肠脱落我也不清楚,机试考试估计够呛,╮(╯▽╰)╭~~~~(>_<)~~~~

 

普里姆堆优化不需要memset初始最大距离,因为一开始就只把顶点1到其他点的路径,压入队列即可,那个G memset 0是清空vector,输入的时候赋值就行了,不需要初始化无穷大,本质是优先队列里有的都是实实在在有路径的,不存在没路的无穷大这种东西。而普里姆朴素版是没路的无穷大和有路的都加进去了

 

现在想下普里姆朴素版

涉及到double无穷大,之前博客提到过,用0x42或者0x7f,tmd全网互相抄,真的恶心,找的真费劲,基本都是所答非所问,要么就是给你一堆狗屁不通的玩意,直接给出答案就得了,很多都给出一堆机器语言还是啥,底层代码之类的,狗东西艹!!好不容易找到好博客学到了:

intINT_MIN,头文件头文件limits.h

doubleDBL_MAX,头文件含头文件float.h

简单明了,这不比那些狗逼博主写的清楚吗

 

 

一次AC

AC代码 —— 普里姆 朴素版 写法

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<math.h>
 5 #include<queue>
 6 #include<vector>
 7 #include<iomanip>
 8 #include<limits.h>
 9 #include<float.h>
10 using namespace std;
11 #define MAX 101
12 int T;
13 int C;
14 struct Node_date{
15     int x;
16     int y;
17 }map[MAX];
18 double G[MAX][MAX];
19 double lowdis[MAX];
20 int is_in[MAX];
21 int main()
22 {
23     cin>>T;
24     while(T--){
25         memset(G,0x7f,sizeof(G));
26         cin>>C;
27         for(int i=1;i<=C;i++)
28             cin>>map[i].x>>map[i].y;
29 
30         for(int i=1;i<=C;i++)
31             for(int j=1;j<=C;j++){
32 //                if(i==j)//自身到自身不需要,当作无穷大就行
33 //                    continue;
34                 double dis;
35                 dis=sqrt(pow((map[i].x-map[j].x),2)+pow((map[i].y-map[j].y),2));
36                 if(dis<10 || dis>1000)
37                     continue;
38                 G[i][j]=dis;
39             }
40         //处理数据结束,每个边都是符合条件的
41 
42         //开始普里姆
43         memset(is_in,0,sizeof(is_in));
44         is_in[1]=1;
45         memset(lowdis,0x7f,sizeof(lowdis));
46         for(int i=1;i<=C;i++)
47             lowdis[i]=G[1][i];
48 //        lowdis[1]=0;
49 
50         double ans=0;
51         int num_edge=0;//不满足C-1条边就不行
52         for(int k=1;k<=C-1;k++){
53             double min=DBL_MAX;
54             int minid;
55             for(int i=1;i<=C;i++){
56                 if(is_in[i]==0 && min>lowdis[i]){
57                     min=lowdis[i];
58                     minid=i;
59                 }
60             }
61 //            cout<<"#"<<min<<endl;
62 //            if(min==1.38242e+306)//但科学计数法表示又不可能用==来判断
63 //                continue;
64 //            注意一点,如果题目的第二个样例,会输出inf,因为距离不符合修桥条件就continue了,但初始化dis0x7f了
65 //            而0x7f又比DBL_MAX大,所以这里其实对于第二个样例每次都是0x7f这个所谓的初始化无穷大进来,导致
66 //            num_edge每次都是C-1
67 
68             if(min>1000)
69                 continue;
70 
71             is_in[minid]=1;
72             num_edge++;
73             ans+=lowdis[minid];
74             for(int i=1;i<=C;i++){
75                 if(is_in[i]==0 && lowdis[i]>G[minid][i]){
76                     lowdis[i]=G[minid][i];
77                 }
78             }
79         }
80 
81 
82         ans=ans*100;
83         if(num_edge==C-1)
84             cout<<fixed<<setprecision(1)<<ans<<endl;//GPT告诉我的
85         else
86             cout<<"oh!"<<endl;
87 
88     }
89 }

再解释一下普里姆的朴素版和堆优化版,堆优化由于vector存图,数据都是有的,即有路的数据才会加进队列,也就不需要考虑什么无穷大的事,

而朴素版由于是邻接矩阵建图,需要把顶点1出发的所有边都存进去,那么有路的就是实际数值,没路的就是无穷大,那为啥费个劲存没路的边呢?我能不能用vector存呢?其实这个就是堆优化的思想了,只不过,堆优化vector写法明显是遍历的边,复杂度涉及边,邻接矩阵朴素版遍历的是点,复杂度只跟点有关系。

 

AC代码 —— 克鲁斯卡尔 写法

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<iostream>
 4 #include<math.h>
 5 #include<algorithm>
 6 #include<queue>
 7 #include<vector>
 8 #include<iomanip>
 9 #include<limits.h>
10 #include<float.h>
11 using namespace std;
12 #define Edge_MAX 2*((1+(100-1))*(100-1)/2) +1   //乘2是因为uv、vu要存双向
13 #define Node_MAX 101
14 int T;
15 int C;
16 struct Node{
17     int x;
18     int y;
19 }map[Node_MAX];
20 
21 struct Edge{
22     int from;
23     int to;
24     double dis;
25 }edge[Edge_MAX];
26 
27 int is_in[Node_MAX];
28 int pre[Node_MAX];
29 int find(int x)
30 {
31     if(x==pre[x])
32         return x;
33     pre[x]=find(pre[x]);
34     return pre[x];
35 }
36 
37 bool cmp(Edge a, Edge b)
38 {
39     if(a.dis!=b.dis)
40         return a.dis<b.dis;
41     else
42         return a.dis<b.dis;
43 }
44 int main()
45 {
46     cin>>T;
47     while(T--){
48         cin>>C;
49         for(int i=1;i<=C;i++)
50             pre[i]=i;
51 
52         for(int i=1;i<=C;i++)
53             cin>>map[i].x>>map[i].y;
54 
55         int num_of_edge=0;
56         for(int i=1;i<=C;i++)
57             for(int j=1;j<=C;j++){
58                 if(i==j)
59                     continue;
60                 double dis;
61                 dis=sqrt(pow((map[i].x-map[j].x),2)+pow((map[i].y-map[j].y),2));
62                 if(dis<10 || dis>1000)
63                     continue;
64                 edge[num_of_edge].from=i;
65                 edge[num_of_edge].to=j;
66                 edge[num_of_edge].dis=dis;
67                 num_of_edge++;
68             }
69 //处理数据结束,每个边都是符合条件的,边数num_of_edge从0开始计数
70 
71 //开始克鲁斯卡尔
72         sort(edge,edge+num_of_edge,cmp);
73 
74         double ans=0;
75         int num=0;
76         for(int i=0;i<num_of_edge;i++){
77             int from=edge[i].from;
78             int to=edge[i].to;
79             double dis=edge[i].dis;
80 
81             int root1=find(from);
82             int root2=find(to);
83             if(root1==root2)
84                 continue;
85             pre[root1]=root2;
86             ans+=dis;
87             //此时连上一条边就num+1
88             num++;
89         }
90 
91         ans=ans*100;
92         if(num==C-1)
93             cout<<fixed<<setprecision(1)<<ans<<endl;
94         else
95             cout<<"oh!"<<endl;
96 
97     }
98 }

克鲁斯卡尔也不涉及无穷大的事,因为你有路就存,没路就不存,路就是边

 

posted @ 2024-12-12 19:47  GerJCS  阅读(6)  评论(0)    收藏  举报