图算法-用Dijkstra和Prim求最小路径
解决问题:
在一个树中有多个节点和多条有向或无向的边,若我们给各个边附上权值,那么利用这两个算法可以计算从一个节点到所有节点的最小距离。实际中可以解决比如快递在区域配送最短的路径等问题。
Dijkstra
思路
例如以第1个节点为树根并循环整个树的节点,找到与1连接的所有点然后比较到这个点的距离是否小于原点(节点1)到这个点的距离(此时还没有),若此时的距离比另一条路到这个点的距离小,则存入数据,以此类推。
具体如下:
我们用dist数组来存入原点到所有点的最小距离,最开始的时候设置所有的点到原点的距离为无穷大。
1 2
| memset(dist, 0x3f, sizeof(dist)); dist[1] = 0;
|
用state数组来记录这个点是否已经找到了到原点的最小距离,如果是则记下1,否则是0。
首先用for遍历每个点,寻找到每一个点的到原点的距离。
再在用一个for来确定这个点是到原点的最短距离。
1 2 3 4 5 6 7 8 9 10 11
| for(int i = 0; i < n; i++){ int t = -1; for(int j = 1; j <= n; j++){ if(!state[j] && (t == -1 || dist[j] < dist[t])) t = j; } state[t] = 1; for(int j = h[t]; j != -1; j = ne[j]){ int i = e[j]; dist[i] = min(dist[i], dist[t] + w[j]); } }
|
整个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| void Dijkstra(){ memset(dist, 0x3f, sizeof(dist)); dist[1] = 0; for(int i = 0; i < n; i++){ int t = -1; for(int j = 1; j <= n; j++){ if(!state[j] && (t == -1 || dist[j] < dist[t])) t = j; } state[t] = 1; for(int j = h[t]; j != -1; j = ne[j]){ int i = e[j]; dist[i] = min(dist[i], dist[t] + w[j]); } } }
|
Prim
思路
prim 算法干的事情是:给定一个无向图,在图中选择若干条边把图的所有节点连起来。要求边长之和最小。在图论中,叫做求最小生成树。prim算法相比于dijkstra是贪心的,它要寻找的是每一个点之间的最短距离。
具体如下
与Dijkstra一样用dist存入距离,并最开始用无穷大赋值。
用state数组来记录这个点是否已经找到了到原点的最小距离,如果是则记下1,否则是0。
用pre数组来保存节点是和谁连通的。
伪代码:
1 2 3 4 5 6 7 8
| int dist[n],state[n],pre[n]; dist[1] = 0; for(i : 1 ~ n) { t <- 没有连通起来,但是距离连通部分最近的点; state[t] = 1; 更新 dist 和 pre; }
|
我们在prim里面相当于是将树的每个点一个一个加入到一个集合中,这个集合就是生成的树。每次选择树都要看看是否在树中并且到树的距离最短,如果是则加入到集合中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| void prim(){ memset(dt,0x3f, sizeof(dt)); int res= 0; dt[1] = 0; for(int i = 0; i < n; i++){ int t = -1; for(int j = 1; j <= n; j++){ if(!st[j] && (t == -1 || dt[j] < dt[t])) t = j; } if(dt[t] == 0x3f3f3f3f) { cout << "impossible"; return; } st[t] = 1; res += dt[t]; for(int i = 1; i <= n; i++){ if(dt[i] > g[t][i] && !st[i]){ dt[i] = g[t][i]; pre[i] = t; } } } cout << res; }
|