Dijkstra+DFS模板总结
来源:互联网 发布:js数组和对象 编辑:程序博客网 时间:2024/05/22 01:31
关于Dijstra的初级运用是,在第一标尺的基础上有下面三个角度:
- 边权:c[maxn] = {maxn}, cost[manx][maxn] = {inf};
- 点权:w[maxn] = {0}, weight[maxn] = {0};
- 最短路径条数:num[maxn] = {0};
a1003.cpp 用到了其中的两个,作为模板来刻意练习,练习如何将问题结构化,模板化。
再额外补充边权的代码,不是这道题的,但是加进来使其完整。
#include <stdio.h>#include <string.h>#include <algorithm>using namespace std;const int maxv = 510; // 最大顶点数const int inf = 1 << 30;int G[maxv][maxv], weight[maxv],cost[maxv][maxv]; // 定义图的邻接矩阵存法,点权int d[maxv] = {inf} ; //记录从起点s到u的最短距离,int n,m,st,ed; // n:顶点数,m:边数int w[maxv],num[maxv]c[manv]; // w:记录最大点权之和,num:最短路径条数bool vis[maxv] = {false}; // 标记是否已经访问// 最朴素的迪杰斯塔拉就是为了找到最短路径,最终效果是更新了d数组,// 但是在产生d数组的过程中,有意思的事情正在发生void Dijkstra(int s) { // 初始化 fill(d,d + maxv, inf); // fill(num,num + maxv, 0); fill(w,w + maxv, 0); d[s] = 0; w[s] = weight[s]; num[s] = 1; // 循环n次 for(int i = 0; i < n; i++) { // 找到u和最小值 int u = -1, MIN = inf; for(int j = 0; j < n; j++) { if(vis[j] == false && d[j] < MIN) { u = j; MIN = d[j]; } } if(u == -1) return; // 没有找到 vis[u] = true; // 找到了就标记为已经访问 // 优化路径长度 for(int v = 0; v < n; v++) { if(vis[v] == false && G[u][v] != inf) { if(d[u] + G[u][v] < d[v]) // 这个条件拿下来用 { d[v] = d[u] + G[u][v]; // 覆盖 c[v] = c[u] + cost[u][v]; // 补充的边权 w[v] = w[u] + weight[v]; // 最短路径的权重更大 num[v] = num[u]; // 三个基础问题中唯一一个用到继承的概念的 } else if(d[u] + G[u][v] == d[v]) // 最短路径有相同的,这时候就看点权之和 { if(w[u] + weight[v] > w[v]) { w[v] = w[u] + weight[v]; } if(c[u] + cost[u][v] < c[v]) { c[v] = c[u] + cost[u][v]; } num[v] += num[u]; } } } }}int main(){ scanf("%d%d%d%d", &n, &m, &st, &ed); for(int i = 0; i < n; i++) { scanf("%d",&weight[i]); } int u, v; fill(G[0],G[0] + maxv * maxv, inf);// 初始化二维数组的写法 for(int i = 0; i < m; i++) { scanf("%d%d", &u, &v); scanf("%d", &G[u][v]);//读入边权 G[v][u] = G[u][v]; } Dijkstra(st); printf("%d %d\n",num[ed],w[ed]); return 0;}
为了理解Dijkstra + DFS,需要首先消化掉只在第一标尺下的DFS与记录前驱的pre数组的方式,然后才能更好的理解多个标尺下的Dijkstra + DFS的思路。
void DFS(int s, int v) // s是起点编号, v是当前访问的顶点编号,这个递归函数目的是为了求s到v的路径{ // 前提是pre数组准备好,pre[i] = i,初始化时是自身是自身的前驱(联想到并查集啦) // 递归边界 if(v == s) { printf("%d\n", s); return; } DFS(s,pre[v]); printf("%d\n",v);}
单纯的用递归函数的设计逻辑来理解这个问题,就会很简单:
- 递归边界:v == s,即起点和终点重合了,自然要输出,并结束本层函数
- 递归式:起点是固定的,是fixed,第二个函数是终点往前挪到前驱
- 本层逻辑:递归回来,要输出当前这个结点的编号
用Dijkstra + DFS组合解题的情景是解脱出在Dijikstra时要处理的逻辑较为复杂,这里的说的复杂逻辑不是DIjkstra本身,Dijkstra的框架是非常简洁优美且代码好写的。
如果只在Dijkstra中记录所有最短路径,然后再在DFS中根据其他标尺求出最优路径,这种做法也非常符合关注点分离的思想。
所以,首先看第一个问题:如何在Dijkstra中记录所有最短路径。
vector<int> pre[maxn] // 存储多条最短路径:仅在第一标尺--距离的指引下
在这种记录多条最短路径的pre数组中,开始不用初始化,看到代码会更清晰:
if(d[u] + G[u][v] < d[v]){ d[v] = d[u] + G[u][v]; pre[v].clear(); // 清空,此时找到更好的了 pre[v].push_back(u); } else if(d[u] + G[u][v] == d[v]){ pre[v].push_back(u); // 令v的前驱为u}
关于为什么在找到更小的距离时清空pre[v]数组,是因为pre[v]存的是v这个顶点距离最小的前驱,那么现在找到了更小的,意味着原来的记录的前驱没用了,自然清空。也因为此,pre数组开始不必初始化。
现在数组已经准备好,开始写DFS遍历所有最短路径,依据其他标尺选择最优。
int optValue;vector<int> pre[maxv];vector<int> path, tempPath;void DFS(int v){ if(v == st) // st是起点 { tempPath.push_back(v); // 将起点st加入临时路径tempPath的最后面 int value; // 存放临时路径的第二标尺值 // 计算tempPath上的value值 if(value 优于 optValue) { optValue = value; path = tempPath; } tempPath.pop_back(); return; } tempPath.push_back(v); // 将当前访问结点加入临时路径tempPath最后面 for(int i = 0; i < pre[v].size(); i++) { DFS(pre[v][i]); } tempPath.pop_back();// 遍历完所有前驱结点,将当前结点v删除}
注意到push_back和pop_back是成对出现的这里。
这是形式上的准确识别记忆,那么如何理解呢?
其实非常简洁,tempPath存储的是一条路径 ,即需要考察起点st到当前结点之间的某个标尺值,tempPath因为是采用递归写法,所以是倒着的:从v到st。
本层逻辑是先把v加入进来,然后递归v的前驱,刚好前驱是往前(向着起点)走。我们先忽略掉递归式,看到最后一句pop_back,这样就能保证执行完一次递归获得一条路径后,tempPath被清空。
而至于递归边界中的一对,是因为本层逻辑中无法把起始点加入tempPath,所以这里需要临时用到,所以临时加进来。
这样Dijkstra + DFS模板的问题框架与部分细节就搭起来了,再来看最初提到的三个基础问题这里如何计算:
- 点权之和
- 边权之和
- 最小路径条数
最简单的是最小路径条数:用一个全局变量num = 0, 在递归边界中num++即可。
点权之和与边权之和都是上面模板中的value,具体写法也很简单,就是遍历tempPath数组。注意,path是存的几种标尺综合的最优解。
// 边权之和:注意是i > 0int value = 0;for(int i = tempPath.size() - 1; i > 0; i++){ int u = tempPath[i], v = tempPath[i - 1]; value += G[u][v];// G[u][v]是边权}// 点权之和int value = 0;for(int i = tempPath.size() - 1; i >= 0; i++){ int u = tempPath[i]; value += weight[u];// weight是边权}
- Dijkstra+DFS模板总结
- dijkstra+dfs模板
- dijkstra+dfs
- dijkstra 模板
- dijkstra 模板
- Dijkstra模板
- dijkstra模板
- dijkstra模板
- Dijkstra模板
- (Dijkstra模板)
- Dijkstra模板
- Dijkstra模板
- Dijkstra 模板
- Dijkstra模板
- dijkstra 模板
- dijkstra模板
- dijkstra模板
- dijkstra模板
- HDU1147
- Red Hat Enterprise Linux 7.3
- 字符指针的可怕
- 网络流学习笔记2
- POJ2566:Bound Found(尺取法)
- Dijkstra+DFS模板总结
- kafka自定义Integer序列化类
- 自己写一个数字地球
- java设计模式--组合模式(结构型)
- 动态存储网络——问答系统
- 多态+菱形虚拟继承(下)
- sublime中pre代码片段的设置
- java的blockingqueue
- Java 选择排序