HDU1875-畅通工程再续
再切一个最小生成树吧
这个题来自于2008浙大研究生复试热身赛(2)—— 全真模拟,好奇怪,复试居然还有热身赛
就是正常走最小生成树,多两个判断:
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全网互相抄,真的恶心,找的真费劲,基本都是所答非所问,要么就是给你一堆狗屁不通的玩意,直接给出答案就得了,很多都给出一堆机器语言还是啥,底层代码之类的,狗东西艹!!好不容易找到个好博客,学到了:
int用INT_MIN,头文件头文件limits.h
double用DBL_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 }
克鲁斯卡尔也不涉及无穷大的事,因为你有路就存,没路就不存,路就是边