【区别】最短路&最小生成树
来源:互联网 发布:单片机应用技术c语言版 编辑:程序博客网 时间:2024/05/22 18:23
一句话概括:最小生成树是计算从一节点到另一节点的最小边集;最短路是带权路径,计算权值最小。也就是说,最小生成树要经过每一个点,而最短路只需要能达到某两点,路径权值最小即可!
两个算法具有相当大的相似性,而且都用到了贪心思想,所以把他们放到一起。
【最短路】常用的算法有dijkstra,bellman-ford,floyd,而【最小生成树】则是prim和kruskal。下面是各个算法的模板。
【Dijkstra复杂度O(n^2)】
#include<stdio.h> //最短路 #define maxsum 0x3fffffff int map[101][101],dist[101],s[101]; void Dijkstra(int n,int x) //n,1,递推实现 { int mindis,u,i,j; for(i=1;i<=n;i++) { dist[i]=map[i][x];//map[x][i] is OK,dist表示i到原点的最小距离! s[i]=0; //printf("%d/n",dist[i]); } s[x]=1;// x=1 for(i=1;i<=n;i++) { mindis=maxsum; u=-1; // 找出当前未使用的点j的dist[j]最小值 for(j=1;j<=n;j++) if(s[j]==0 && dist[j]<mindis)//------------(2) { u=j; //保存当前邻接点中最小的 mindis=dist[j]; } s[u]=1;//u点已存入s集合,最小的!所以放外面 //更新dist for(j=1;j<=n;j++) //-------------------(3) if(s[j]==0) if( dist[u]+map[u][j]<dist[j] && map[u][j]<maxsum) //从u点发散出去寻找,map[u][j]<maxsum存在权值 ,map[j][u] is OK dist[j]=dist[u]+map[u][j]; } } int main() { int n,m,a,b,c,i,j; while(scanf("%d%d",&n,&m)!=EOF) { if(n==0&&m==0) break; for(i=1;i<=n;i++) for(j=1;j<=n;j++) map[i][j]=maxsum;//初始化邻接矩阵 for(i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); map[a][b]=map[b][a]=c;//构造邻接矩阵,对称的!无向! } Dijkstra(n,1); printf("%d/n",dist[n]); } return 0; }
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
【最小生成树的prim模板:复杂度O(n^2)】
#include<iostream>//最小生成树#define INF 0x1f1f1f1f#define M 1000using namespace std;double dis[M],map[M][M];bool flag[M];int prim(int s,int n) //s为起点,n为点的个数{ int i,j,k,temp,md,total=0; for(i=1;i<=n;i++) dis[i]=map[s][i]; //与最短路不同,而是将dis置为map[s][i] memset(flag,false,sizeof(flag)); flag[s]=true; //将起点加入集合 for(i=1;i<n;i++){ //依旧进行n-1次迭代,每次找到不在集合的最小边(n个点有n-1条边)!!!!!! md=INF; for(j=1;j<=n;j++){ if(!flag[j]&&dis[j]<md){ md=dis[j]; temp=j; } } flag[temp]=true; //将找到的最小边的点加入集合 total+=md; //并将这个边的权值加到total中 for(j=1;j<=n;j++) //松弛操作,注意与最短路不同 if(!flag[j]&&dis[j]>map[temp][j]) dis[j]=map[temp][j]; } return total;}
【Kruskal最小生成树模板 复杂度O(E*logE)】
typedef struct edge{ int a; int b; int value;}edge;edge edges[earraysize];int final[narraysize]; //存储父节点 中括号里面是儿子,外面是父亲int nodecount[narraysize]; //存储该节点孩子结点的个数 bool cmp(edge a,edge b){ return a.value<b.value;}int findp(int x) //寻找父亲{ while(x!=fa[x]) x=fa[x]; return x;}bool Union(int x,int y) //合并 { int rootx=findp(x); /*为什么要找父亲?因为要判是否有回路,假如父亲相同,而x跟y连通,那么就形成了回路*/ int rooty=findp(y); if(rootx==rooty) return false; else if(nodecount[rootx]<=nodecount[rooty]) //优化,把深度小的子树加到深度大的子树,减少树的高度 { final[rootx]=rooty; /*其实不优化也可以直接final[rootx]=rooty或者final[rooty]=rootx也ok */ nodecount[rooty]+=nodecount[rootx]; } else { final[rooty]=rootx; nodecount[rootx]+=nodecount[rooty]; } return true;}int main (){ //freopen("a.txt","r",stdin); int num=0; int n,m; int i,j; while ( scanf ( "%d%d", &n, &m ) != EOF ) { num=0; //记录生成树中的边的数目 for(i=1;i<=m;i++) { scanf("%d%d%d",&edges[i].a,&edges[i].b,&edges[i].value); } for(i=1;i<=n;i++) //初始化 { final[i]=i; nodecount[i]=1; } sort(edges+1,edges+m+1,cmp); //排序 for(i=1;i<=m;i++) //遍历所有的边 { if(Union(edges[i].a,edges[i].b)) //合并 { num++; } if(num==n-1) //找到了最小生成树 break; } } return 0;}
*****************************************************************************************************************************************************************************************
*****************************************************************************************************************************************************************************************
下面是最短路的bellmen-ford算法,与dijkstra不同,bellman-ford可以运用于有负权值的图,不过复杂度很高,O(VE )... 慎用~(可以用SPFA,它bwllman-ford的扩展)
Bellman-ford算法同样是对每条边进行N-1次松弛,当有权值为负时,对所有边进行N-1次松弛,如果dis还能更新,说明有负环。
ps:名词解释【负环】 在一个图里每条边都有一个权值(有正有负)
如果存在一个环(从某个点出发又回到自己的路径),而且这个环上所有权值之和是负数,那这就是一个负权环,也叫负权回路
存在负权回路的图是不能求两点间最短路的,因为只要在负权回路上不断兜圈子,所得的最短路长度可以任意小。
Bellman-ford原理:
1.如果最短路存在,则每个顶点最多经过一次,因此不超过n-1条边;
2.长度为k的路由长度为k-1的路加一条边得到;
3.由最优性原理,只需依次考虑长度为1,2,…,k-1的最短路。
Bellman-ford模板:
#include<stdio.h>//最短路#include<string.h>#define INF 0x1f1f1f1f#define MAX 102#define MAXM 20008int dist[MAX];struct Edge{ //边结构体定义 int u, v, w; Edge(){} Edge(int a, int b, int c):u(a), v(b), w(c){}}edge[MAXM];int bellman_ford(int n, int m, int s) //n个点、m条边、s为起点 { memset(dist, 0x1f, sizeof(dist)); //初始化距离很大 dist[s] = 0; int i, j, u, v, f; for (i = 1; i < n; ++i) //迭代 n - 1 次,对每条边进行n-1次松弛 { f = 0; for (j = 0; j < m; ++j) { u = edge[j].u; v = edge[j].v; if (dist[v] > dist[u] + edge[j].w) // 松弛操作 { dist[v] = dist[u] + edge[j].w; f = 1; } } if (!f) return 1; //如果其中一次迭代没改变,停止 } for(j = 0; j < m; ++j) //再进行一次迭代 { u = edge[j].u; v = edge[j].v; if (dist[v] > dist[u] + edge[j].w) //若还能松弛, 则存在负环 return -1; //存在负环返回 -1 } return 1; //没有负环返回 1 }
算法结束后dist数组已经是最短路径。
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
【SPFA模板: 期望的时间复杂度O(KE), 其中K为所有顶点进队的平均次数,可以证明K一般小于等于2。】
#include<iostream>//最短路 #include<string.h> #include<cstdio> #include <vector> #include <queue> using namespace std; #define N 50001int INF = 0x7fffffff; int n,m; typedef struct edge { int to; int w; }edge,temp; vector<edge> adjmap[N]; //vector实现邻接表 int d[N]; bool vis[N]; //记录顶点是否在队列中,SPFA算法可以入队列多次 int cnt[N]; //记录顶点入队列次数 void SPFA() { queue<int> myqueue; int i; for(i=1;i<=n;++i) d[i] = INF; //将除源点以外的其余点的距离设置为无穷大 memset(vis,0,sizeof(vis)); memset(cnt,0,sizeof(cnt)); d[1]=0; //源点的距离为0 vis[1] = true; cnt[1]++; //源点的入队列次数增加 myqueue.push(1); int topint; while(!myqueue.empty()) { topint = myqueue.front(); myqueue.pop(); vis[topint] = false; for(i=0;i<adjmap[topint].size();++i) { int to = adjmap[topint][i].to; if(d[topint]<INF && d[to]>d[topint]+ adjmap[topint][i].w) { d[to] = d[topint]+ adjmap[topint][i].w; if(!vis[to]) { vis[to] = true; cnt[to]++; if(cnt[to]>=n) //当一个点入队的次数>=n时就证明出现了负环。 return ; myqueue.push(to); } } } } printf("%d/n",d[n]); } int main() { //freopen("a.txt","r",stdin); scanf("%d%d",&n,&m); int i; int s,e,w; edge temp; for(i=1;i<n+1;++i) //此处特别注意对邻接表清空 adjmap[i].clear(); for(i=0;i<m;++i) //双向 { cin>>s>>e>>w; temp.to = e; temp.w = w; adjmap[s].push_back(temp); temp.to = s; adjmap[e].push_back(temp); } SPFA(); return 0; }
- 【区别】最短路&最小生成树
- 区别 最短路跟最小生成树
- 最短路(最小生成树)
- 最小生成树与最短路的区别
- BOJ 333 最小生成树+最短路
- 图论 最小生成树 和最短路
- 最小生成树&最短路模板
- 【模板】最短路&&最小生成树
- 最短路与最小生成树
- uva 10816 最小生成树 + 最短路
- poj 3026 BFS + prim(最短路最小生成树的区别)
- 并查集&最小生成树、最短路
- 图论--最小生成树和最短路1
- POJ 1797 Heavy Transportation(最小生成树或最短路)
- BZOJ 4144: [AMPPZ2014]Petrol 最短路+最小生成树+倍增
- bzoj 4144 [AMPPZ2014]Petrol 最短路+最小生成树+倍增
- 【BZOJ 4144】[AMPPZ2014]Petrol 最短路+最小生成树
- 最小生成树 最短路 并查集
- GUI 中的部分小功能实现
- 关于迭代器失效
- 表达式计算过程中的类型转换,经常被忽略的问题
- 基本知识
- 整洁代码
- 【区别】最短路&最小生成树
- C#中解析XML的内容
- AOP的XML方式的实现
- 标准c++中string类介绍
- CE6.0 下获得 SD 卡序列号的方法
- TCP/IP SOCKET HTTP
- bochs的检测我一下子没领悟过来
- [StoryToolKit]第一个demo
- PRINTF()输出格式控制