prim最小生成树
2017-01-17 22:21
281 查看
一、关于最小生成树的问题引入
布线问题
比如随意给你很多个点,你要将他们全部连起来,要连接n个点,如何只使用n-1根连线,并且尽量使得连线总距离最短
将这问题用一个连通无向图来表示。
我们希望在很多的连通方式中,找到一个无环的连通方式,既能够将所有结点连接起来,又具有最小的权重。由于T是无环的,并且连通所有的结点,因此T必然是一颗树。我们称这样的树为生成树。求取生成树的问题为最小生成树问题。比如如下这张图。
二、关于prim算法的思路
这是一个典型的贪心算法,由局部最优推导出全局最优。把每次已经点亮的节点、所有可到达的边中权重最小的边选入(注意,不能构成回路)
(1)假设从a开始点亮,到b是4,到h是8,那么选择b
(2)a b都被点亮,且两个到c和h都是最小边8,都可以选择,这时候就看你的代码顺序了,并无妨碍
(3)接下来abc点亮,他们三个连接的最小边,是c到i,接下来几步稍加省略了
(4)到(g)图这个时候注意,不能选择h和i,因为他们会构成回路,应该选择c与d
简单解释一下为什么可以用贪心算法,因为每一次你选进来的边是采用局部最优的方法选取的 ,最短而又不构成回路。而全局来看,这条边又不得不选。恰好吻合。
最后是接下来要用到的邻接矩阵
三、代码
四、案例测试
(输入格式)
9 14
1 2 4
1 8 8
2 3 8
2 8 11
3 4 7
3 6 4
3 9 2
4 5 9
4 6 14
5 6 10
6 7 2
7 8 1
7 9 6
8 9 7
(因为自己测试时候用了ifstream,这里直接输出结果)
布线问题
比如随意给你很多个点,你要将他们全部连起来,要连接n个点,如何只使用n-1根连线,并且尽量使得连线总距离最短
将这问题用一个连通无向图来表示。
我们希望在很多的连通方式中,找到一个无环的连通方式,既能够将所有结点连接起来,又具有最小的权重。由于T是无环的,并且连通所有的结点,因此T必然是一颗树。我们称这样的树为生成树。求取生成树的问题为最小生成树问题。比如如下这张图。
二、关于prim算法的思路
这是一个典型的贪心算法,由局部最优推导出全局最优。把每次已经点亮的节点、所有可到达的边中权重最小的边选入(注意,不能构成回路)
(1)假设从a开始点亮,到b是4,到h是8,那么选择b
(2)a b都被点亮,且两个到c和h都是最小边8,都可以选择,这时候就看你的代码顺序了,并无妨碍
(3)接下来abc点亮,他们三个连接的最小边,是c到i,接下来几步稍加省略了
(4)到(g)图这个时候注意,不能选择h和i,因为他们会构成回路,应该选择c与d
简单解释一下为什么可以用贪心算法,因为每一次你选进来的边是采用局部最优的方法选取的 ,最短而又不构成回路。而全局来看,这条边又不得不选。恰好吻合。
最后是接下来要用到的邻接矩阵
三、代码
#include<iostream> #include<cstring> #include<vector> #include<fstream> using namespace std; const int maxn=100; //顶点数 const int INF=0x7fffffff; void inputgraph (); //初始化并邻接表存图 void prime(); struct edge //边 { //int from不需要;因为edge在主函数会创建graph的二维数组,而from就是每个二维数组的行值 int to; //到达的点 int cost; //边的权重或称花费 bool flag; //是否入选 }; int choosed[maxn]; //已选顶点 int nodeNum,edgeNum,MST; //顶点数、边数、最小生成树 bool scan[maxn];//是否已经扫描(点亮) vector <edge> graph[maxn];//邻接图表 int main() { while(scanf("%d%d",&nodeNum,&edgeNum)==2) //输入图的点数、边数 { inputgraph(); prime(); } system("pause"); return 0; } void inputgraph () //初始化邻接表 { //ifstream fin; //fin.open("input.txt"); for(int j=0;j<maxn;j++) //清空 { graph[j].clear(); } int from,to,cost,num=0; //从from到to花费cost的边 for(int i=0;i<edgeNum;i++) { cin>>from>>to>>cost; //建议把cin改成fin文件输入方式 choosed[num++]=from; // choosed[0]十分重要,其他内容无关紧要因为会再更新。 //开始构建无向图 edge tmp; tmp.flag=false; tmp.cost=cost; tmp.to=to; graph[from].push_back(tmp);//graph[i][j]的出处,i由from表示,j是每一次pushback后的tmp tmp.to=from; //无向图注意from和to同等地位处理 graph[to].push_back(tmp); } } void prime() //prime算法 { MST=0; memset(scan,false,sizeof(scan)); scan[choosed[0]]=true; //初始点已经点亮 int choosedNum=1,from,to,cost,index; while(choosedNum<nodeNum) { cost=INF; for(int i=0;i<choosedNum;i++) //每选入一个点,就多一次的扫描 { for(int j=0;j<graph[choosed[i]].size();j++) //扫描一个点亮的节点 { edge tmp=graph[choosed[i] ][j]; //把表中的这个edge拿去接下来的三个if判断一下 if(!scan[tmp.to] && !tmp.flag && tmp.cost<cost) // 如果这个节点想要去的to节点没有被点亮过(避免回路),如果这个节点没有被选,如果这个节点代价小 { from=choosed[i];//看一下这个from to=tmp.to; cost=tmp.cost; index=j; } } } graph[from][index].flag=true; //将找到的边标志为已选 //无向图,从from->to的边已选,那么从to->from的边也已选 for(int k=0;k<graph[to].size();k++) { if(graph[to][k].to==from) { graph[to][k].flag=true; break; } } cout<<"第"<<choosedNum<<"引进边的重量为"<<cost<<endl; choosed[choosedNum++]=to; //将选择的点亮的点放到已选点放到已选的集合中 scan[to]=true; MST+=cost; //最小生成树费用增加 } printf("%d\n",MST); }
四、案例测试
(输入格式)
9 14
1 2 4
1 8 8
2 3 8
2 8 11
3 4 7
3 6 4
3 9 2
4 5 9
4 6 14
5 6 10
6 7 2
7 8 1
7 9 6
8 9 7
(因为自己测试时候用了ifstream,这里直接输出结果)
相关文章推荐
- hdoj 2122Ice_cream’s world III 【最小生成树 kruskal && prim】
- 最小生成树kruskal+prim
- Highways POJ - 2485 最小生成树 Prim
- POJ Problem 1258 Agri-Net 【最小生成树Prim】
- 最小生成树(prim)--poj2377
- 最小生成树——Prim
- 最小生成树——Prim、Kruskal、Sollin(Boruvka)
- HDU4081 Qin Shi Huang's National Road System【prim最小生成树+枚举】
- hdu 1162 Eddy's picture(最小生成树,prim)
- 【算法】最小生成树之prim
- 简单图论-最小生成树—kruskal+prim
- 数据结构 学习笔记(九):图(下):最小生成树(Prim,Kruskal 算法),拓扑排序 AOV,关键路径 AOE
- POJ 1258 Agri-Net (prim 最小生成树)
- nyoj 434 Jungle Roads 【prim&&最小生成树】
- 最小生成树prim和kruskal算法
- C++ 最小生成树之Prim(普里姆)算法
- 图论中最小生成树算法-Prim(普里姆)算法、kruskal(克鲁斯卡尔避圈法)算法、破圈算法
- poj 2421 Constructing Roads(最小生成树prim)
- poj 2377-prim(最小生成树的最大边权的和)
- Prim实现最小生成树