求最小生成树(暴力法,prim,prim的堆优化,kruskal)
5 7
1 2 2
2 5 2
1 3 4
1 4 7
3 4 1
2 3 1
3 5 6
我们采用的是dfs的回溯暴力,所以对于如下图,只能搜索到3条路,就是那种dfs的路。
思路:
暴力求最小生成树
求这个图的最小生成树
我就要看有多少个点被选进去了,vis数组就好,并且用个n来表示已经被选的点的个数
然后记录所以已经选了的路径和
1 #include <bits/stdc++.h> 2 #define INFINITE 0x3fffffff 3 using namespace std; 4 struct node{ 5 int v; 6 int w; 7 node(int v,int w){ 8 this->v=v; 9 this->w=w; 10 } 11 }; 12 vector<node> vec[100]; 13 int edgeNum[100]; 14 int n,m; 15 bool vis[100]; 16 17 int minDis=INFINITE; 18 19 20 void addEdge(int u,int v,int w){ 21 edgeNum[u]++; 22 vec[u].push_back(node(v,w)); 23 } 24 25 void init(){ 26 cin>>n>>m; 27 for(int i=1;i<=m;i++){ 28 int u,v,w; 29 cin>>u>>v>>w; 30 addEdge(u,v,w); 31 addEdge(v,u,w); 32 } 33 } 34 35 /* 36 暴力求最小生成树 37 求这个图的最小生成树 38 我就要看有多少个点被选进去了,vis数组就好,并且用个n来表示已经被选的点的个数 39 然后记录所以已经选了的路径和 40 */ 41 //记录起点,记录选了个点 42 void search(int start,int dis,int cur){ 43 //边界一定要清楚 44 //而且这里是5不是6 45 if(cur==5){//这里写成n==n 第几错误傻逼错误满天飞 46 cout<<dis<<endl; 47 if(dis<minDis){ 48 minDis=dis; 49 } 50 return ; 51 } 52 for(int i=0;i<edgeNum[start];i++){ 53 54 int v=vec[start][i].v; 55 int w=vec[start][i].w; 56 //cout<<vis[v]<<endl; 57 if(!vis[v]){ 58 cout<<start<<" "<<v<<" "<<w<<endl; 59 vis[v]=true; 60 search(v,dis+w,cur+1); 61 vis[v]=false; 62 } 63 64 } 65 } 66 67 68 int main(){ 69 freopen("in.txt","r",stdin); 70 init(); 71 vis[1]=true;//这句话居然在search下面 72 search(1,0,1); 73 74 cout<<minDis<<endl; 75 return 0; 76 }
上述代码再说几点
1、上述代码并不能解决最小生成树,只能求出DFS的最小生成树情况,所以还需要其它的暴力方法
2、回溯有问题
3、回溯的边界请想清楚
4、变量的初始值请想清楚,dis初始时0不是INFINITE
5、第71行我开始居然写到了72行之下
6、写代码太不专心了,傻逼错误太多了
7、第45行也是
8、暴力搜索的话应该是搜索已经求出来的集合到未求出来的集合的路径,这样可以暴力求出最小生成树
9、树形dp可以求出最小生产树,自然记忆化递归也可以
代码2,把回溯中的变量dis写在了外面,更好理解回溯
1 #include <bits/stdc++.h> 2 #define INFINITE 0x3fffffff 3 using namespace std; 4 struct node{ 5 int v; 6 int w; 7 node(int v,int w){ 8 this->v=v; 9 this->w=w; 10 } 11 }; 12 vector<node> vec[100]; 13 int edgeNum[100]; 14 int n,m; 15 bool vis[100]; 16 int dis=0; 17 int minDis=INFINITE; 18 19 20 void addEdge(int u,int v,int w){ 21 edgeNum[u]++; 22 vec[u].push_back(node(v,w)); 23 } 24 25 void init(){ 26 cin>>n>>m; 27 for(int i=1;i<=m;i++){ 28 int u,v,w; 29 cin>>u>>v>>w; 30 addEdge(u,v,w); 31 addEdge(v,u,w); 32 } 33 } 34 35 /* 36 暴力求最小生成树 37 求这个图的最小生成树 38 我就要看有多少个点被选进去了,vis数组就好,并且用个n来表示已经被选的点的个数 39 然后记录所以已经选了的路径和 40 */ 41 //记录起点,记录选了个点 42 void search(int start,int cur){ 43 //边界一定要清楚 44 //而且这里是5不是6 45 if(cur==5){//这里写成n==n 第几错误傻逼错误满天飞 46 cout<<dis<<endl; 47 if(dis<minDis){ 48 minDis=dis; 49 } 50 return ; 51 } 52 for(int i=0;i<edgeNum[start];i++){ 53 54 int v=vec[start][i].v; 55 int w=vec[start][i].w; 56 //cout<<vis[v]<<endl; 57 if(!vis[v]){ 58 cout<<start<<" "<<v<<" "<<w<<endl; 59 vis[v]=true; 60 dis+=w; 61 search(v,cur+1); 62 dis-=w; 63 vis[v]=false; 64 } 65 66 } 67 } 68 69 70 int main(){ 71 freopen("in.txt","r",stdin); 72 init(); 73 vis[1]=true;//这句话居然在search下面 74 search(1,1); 75 76 cout<<minDis<<endl; 77 return 0; 78 }
prim
1 #include <bits/stdc++.h> 2 #define INFINITE 0x3fffffff 3 using namespace std; 4 struct node{ 5 int v; 6 int w; 7 node(int v,int w){ 8 this->v=v; 9 this->w=w; 10 } 11 }; 12 vector<node> vct[100]; 13 int edgeNum[100]; 14 int n,m; 15 bool vis[100]; 16 //集合a到集合b的最短距离 17 int dis[100]; 18 int ans[100]; 19 int juli=0; 20 21 void printDis(){ 22 for(int i=1;i<=n;i++){ 23 cout<<dis[i]<<" "; 24 } 25 cout<<endl; 26 } 27 28 void addEdge(int u,int v,int w){ 29 edgeNum[u]++; 30 vct[u].push_back(node(v,w)); 31 } 32 33 void init(){ 34 cin>>n>>m; 35 for(int i=1;i<=m;i++){ 36 int u,v,w; 37 cin>>u>>v>>w; 38 addEdge(u,v,w); 39 addEdge(v,u,w); 40 } 41 } 42 43 void prim(int start){ 44 memset(dis,0x3f,sizeof(dis)); 45 vis[start]=true; 46 dis[start]=0; 47 ans[1]=start; 48 //时间复杂度 e+n^2 49 for(int i=2;i<=n;i++){ 50 //更新 51 for(int j=0;j<edgeNum[start];j++){ 52 int v=vct[start][j].v;//这里的j写成i了 53 int w=vct[start][j].w;//这里的j写成i了 54 if(!vis[v]&&w<dis[v]){ 55 dis[v]=w; 56 } 57 } 58 59 //找最小 60 int minv=INFINITE,min_i; 61 for(int j=1;j<=n;j++){ 62 if(!vis[j]&&dis[j]<minv){//这里的vis写成了dis,这些sb错误真的要爆了我 63 minv=dis[j]; 64 min_i=j; 65 } 66 } 67 juli+=dis[min_i]; 68 vis[min_i]=true; 69 ans[i]=min_i; 70 start=min_i; 71 } 72 } 73 74 void print(){ 75 cout<<juli<<endl; 76 for(int i=1;i<=n;i++){ 77 cout<<ans[i]<<" "; 78 } 79 cout<<endl; 80 } 81 82 83 84 int main(){ 85 freopen("in.txt","r",stdin); 86 init(); 87 prim(1); 88 print(); 89 return 0; 90 }
上面代码说几点:
1、总的思路:集合a到集合b的最短距离,并不是新选的点到其它点的距离
2、还是有超级多傻逼错误
3、52,53行: 这里的j写成i了
4、62行,vis写成dis,其它位置的dis写成vis
5、每一轮里面:更新+找最小
6、加强复习,这个最小生成树真的弄了一万遍了
7、在代码中看时间复杂度,时间复杂度 e+n^2
堆优化的prim
1 #include <bits/stdc++.h> 2 #define INFINITE 0x3fffffff 3 using namespace std; 4 struct node{ 5 int v; 6 int w; 7 node(int v,int w){ 8 this->v=v; 9 this->w=w; 10 } 11 }; 12 vector<node> vct[100]; 13 int edgeNum[100]; 14 int n,m; 15 bool vis[100]; 16 //集合a到集合b的最短距离 17 int dis[100]; 18 int ans[100]; 19 int juli=0; 20 21 void printDis(){ 22 for(int i=1;i<=n;i++){ 23 cout<<dis[i]<<" "; 24 } 25 cout<<endl; 26 } 27 28 void addEdge(int u,int v,int w){ 29 edgeNum[u]++; 30 vct[u].push_back(node(v,w)); 31 } 32 33 void init(){ 34 cin>>n>>m; 35 for(int i=1;i<=m;i++){ 36 int u,v,w; 37 cin>>u>>v>>w; 38 addEdge(u,v,w); 39 addEdge(v,u,w); 40 } 41 } 42 43 struct myCmp{ 44 bool operator ()(int a,int b){//忘记写(),之前写了被用了 45 return dis[a]>dis[b]; 46 } 47 }; 48 priority_queue<int,vector<int>,myCmp> q; 49 void prim1(int start){ 50 int cnt=0; 51 memset(dis,0x3f,sizeof(dis)); 52 q.push(start);//这里写成了push_back 53 dis[start]=0; 54 while(!q.empty()){ 55 int u=q.top(); 56 q.pop(); 57 if(vis[u]) continue; 58 vis[u]=true; 59 ans[++cnt]=u; 60 juli+=dis[u]; 61 for(int i=0;i<edgeNum[u];i++){ 62 int v=vct[u][i].v; 63 int w=vct[u][i].w; 64 if(!vis[v]&&w<dis[v]){ 65 dis[v]=w; 66 q.push(v); 67 } 68 } 69 } 70 71 } 72 73 void print(){ 74 cout<<juli<<endl; 75 for(int i=1;i<=n;i++){ 76 cout<<ans[i]<<" "; 77 } 78 cout<<endl; 79 } 80 81 82 83 int main(){ 84 freopen("in.txt","r",stdin); 85 init(); 86 prim1(1); 87 print(); 88 return 0; 89 }
关于上面代码说几点:
1、因为dijkstra算法可以看成prim算法的特例,因为都是集合到集合的距离,不过dijkstra中的一个集合只有起始元素
2、所以两者算法极其相识,堆优化也是几乎一样
3、傻逼错误还是很多
4、52行的push写成了push_back,因为queue是push,vector是pushback
5、重载()的()写了被用了,所以导致漏掉了
6、时间复杂度:e+nlogn
kruskal
1 #include <bits/stdc++.h> 2 using namespace std; 3 struct edgeNode{ 4 int u; 5 int v; 6 int w; 7 bool vis; 8 bool operator <(const edgeNode &p) const{ 9 return w<p.w; 10 } 11 }edge[100]; 12 //father不是边的father,而是点的father 13 int fa[100]; 14 int n,m; 15 int juli=0; 16 17 void print(); 18 19 void init(){ 20 cin>>n>>m; 21 for(int i=1;i<=m;i++){ 22 int u,v,w; 23 cin>>u>>v>>w; 24 edge[i].u=u; 25 edge[i].v=v; 26 edge[i].w=w; 27 } 28 } 29 30 //int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);} 31 int find(int x){ 32 if(x==fa[x]) return x; 33 return fa[x]=find(fa[x]); 34 } 35 36 void kruskal(){ 37 38 for(int i=1;i<=n;i++) fa[i]=i; 39 for(int i=1;i<=m;i++){ 40 int x=find(edge[i].u); 41 int y=find(edge[i].v); 42 if(x!=y) { 43 edge[i].vis=true; 44 juli+=edge[i].w; 45 //这样就已经实现了把集合相连 46 fa[y]=x; 47 } 48 } 49 } 50 51 int main(){ 52 freopen("in.txt","r",stdin); 53 init(); 54 sort(edge+1,edge+1+m); 55 print(); 56 kruskal(); 57 print(); 58 cout<<juli<<endl; 59 return 0; 60 } 61 void print(){ 62 for(int i=1;i<=m;i++){ 63 cout<<edge[i].u<<" "<<edge[i].v<<" "<<edge[i].w<<" "<<edge[i].vis<<endl; 64 } 65 cout<<endl; 66 }
关于上面代码说几点:
1、father不是边的father,而是点的father
2、46行fa[y]=x;这样就已经实现了把集合相连
3、第31行并查集的两行代码要记下来,一个是叶子,非叶子怎么办
4、第8行结构体里面重载运算符
5、多复习