最短路径问题是指在一个加权有向图或者无向图中,寻找两个顶点之间经过权值和最小的路径。该问题通常被应用于地图导航、物流路线规划等领域。
常见的解决最短路径问题的算法有 Dijkstra 算法和 Floyd-Warshall 算法。
Dijkstra 算法
Dijkstra 算法是以比较当前距离源点最近的顶点来不断扩大最短路径的范围,直到扩大到目标顶点时得到最短路径。具体过程如下:
- 初始化
首先将源点到各个顶点的距离初始化为正无穷大(表示未知),然后将源点到自身的距离初始化为 0。
- 找到当前最短路径的顶点
首先从源点开始,选择一个距离源点最近的未确定最短路径的顶点,将其标记为已确定最短路径。
- 更新相邻顶点的距离
对于当前已确定最短路径的顶点,遍历它的所有相邻顶点(即有直接边相连的顶点),并更新它们到源点的距离。如果通过当前已确定最短路径的顶点,到达相邻顶点的距离比它原有的距离更短,则更新相邻顶点的到源点的距离。
- 重复步骤 2 和步骤 3,直到所有顶点都已确定最短路径或者目标顶点被标记为已确定最短路径。
Floyd-Warshall 算法
Floyd-Warshall 算法则是通过动态规划的方法,计算任意两个顶点之间的最短路径。具体过程如下:
- 初始化
首先将所有顶点之间的距离初始化为正无穷大(表示未知),然后将自身到自身的距离初始化为 0,将所有有直接边相连的顶点之间的距离初始化为它们之间的边权值。
- 逐步缩小范围
对于每个顶点,依次将它作为中转点,计算经过这个中转点时的最短路径,并将结果存储在一个二维数组中。具体实现方式为,枚举任意两个顶点,如果它们之间没有直接边相连,则它们之间的距离为它们通过中转点连接时的距离(即通过中转点到达另外一个顶点的距离加上两个顶点之间的距离)。如果它们之间已经有直接边相连,则它们之间的距离为它们之间的边权值。
- 重复步骤 2,直到所有顶点都作为中转点被考虑过一遍。
总结
Dijkstra 算法主要适用于从一个顶点到其他所有顶点的最短路径问题,而 Floyd-Warshall 算法则适用于任意两个顶点之间的最短路径问题。
Dijkstra 算法示例:
#include <stdio.h>
#include <limits.h>
#include <stdbool.h>
#define INF INT_MAX // 定义正无穷
// 图的邻接矩阵表示
int graph[5][5] =
{
{0, 10, INF, 30, 100},
{INF, 0, 50, INF, INF},
{INF, INF, 0, INF, 10},
{INF, INF, 20, 0, 60},
{INF, INF, INF, INF, 0}
};
// 从未确定最短路径的顶点中,选择一个距离源点最近的顶点
int min_distance(bool spt_set[], int dist[], int n) {
int min_dist = INF, min_index = -1;
int i;
for (i = 0; i < n; i++) {
if (!spt_set[i] && dist[i] < min_dist) {
min_dist = dist[i];
min_index = i;
}
}
return min_index;
}
// 计算从源点到各个顶点的最短路径
void dijkstra(int start, int n) {
bool spt_set[n];
int dist[n];
int i;
// 初始化
for (i = 0; i < n; i++) {
spt_set[i] = false;
dist[i] = INF;
}
dist[start] = 0;
// 计算最短路径
for (i = 0; i < n - 1; i++) {
int u = min_distance(spt_set, dist, n);
spt_set[u] = true;
int v;
for (v = 0; v < n; v++)
{
if (!spt_set[v] && graph[u][v] != INF &&
dist[u] != INF && dist[u] + graph[u][v] < dist[v])
{
dist[v] = dist[u] + graph[u][v];
}
}
}
// 输出结果
printf("从顶点 %d 到各个顶点的最短距离为:\n", start);
for (i = 0; i < n; i++)
{
printf("%d -> %d 的最短距离为 %d\n", start, i, dist[i]);
}
}
int main()
{
dijkstra(0, 5);
return 0;
}
其中,graph 数组是一个 5x5 的邻接矩阵,表示一个包含 5 个顶点的有向图。在实际应用中,需要根据具体的需求来构造相应的图。
在实现中,首先对每个顶点到源点的距离进行初始化,然后选择一个距离源点最近的未确定最短路径的顶点,逐步扩大已确定最短路径的范围。对于每个已确定最短路径的顶点,遍历它的所有相邻顶点,更新它们到源点的距离,直到所有顶点都已确定最短路径。
难度简单
57
给你一个长度为 5 的字符串 time ,表示一个电子时钟当前的时间,格式为 "hh:mm" 。最早 可能的时间是 "00:00" ,最晚 可能的时间是 "23:59" 。
在字符串 time 中,被字符 ? 替换掉的数位是 未知的 ,被替换的数字可能是 0 到 9 中的任何一个。
请你返回一个整数 answer ,将每一个 ? 都用 0 到 9 中一个数字替换后,可以得到的有效时间的数目。
示例 1:
输入:time = "?5:00"输出:2解释:我们可以将 ? 替换成 0 或 1 ,得到 "05:00" 或者 "15:00" 。注意我们不能替换成 2 ,因为时间 "25:00" 是无效时间。所以我们有两个选择。
示例 2:
输入:time = "0?:0?"输出:100解释:两个 ? 都可以被 0 到 9 之间的任意数字替换,所以我们总共有 100 种选择。
示例 3:
输入:time = "??:??"输出:1440解释:小时总共有 24 种选择,分钟总共有 60 种选择。所以总共有 24 * 60 = 1440 种选择。
一开始看简单题直接,就代问号进去,没考虑到数字前后的一个关系,其实一共考虑也就前两个,24进制要考虑下前后,后面两个,一个乘6,一个乘10就行
int countTime(char * time)
{
int sum = 1;
if(time[0] == '?' && time[1] == '?'){
sum = 24;
}
else if(time[0] == '?' && time[1] != '?'){
sum = time[1] < '4' ? 3 : 2;
}
else if(time[0] != '?' && time[1] == '?'){
sum = time[0] < '2' ? 10 : 4;
}
if(time[3] == '?' && time[4] == '?'){
sum *= 60;
}
else if(time[3] == '?' && time[4] != '?'){
sum *= 6;
}
else if(time[3] != '?' && time[4] == '?'){
sum *= 10;
}
return sum;
}
然后官解中有个dfs,可参考参考,但它在这的时间复杂度是比枚举要打的。
方法一:回溯
思路与算法
由于字符串 \textit{time}time 中的 \text{?'}‘?’ 可以被 \text{0'}‘0’ 到 \text{9'}‘9’ 中的任意字符替换,则依次尝试将字符串中的每个 \text{?'}‘?’ 替换为 \text{0'}‘0’ 到 \text{9'}‘9’ 后,并检测该时间的合法性即可,此时合法的时间需要满足如下:
\text{“00"} \le \text{hh} \le \text{“23"}“00"≤hh≤“23";
\text{“00"} \le \text{mm} \le \text{“59"}“00"≤mm≤“59";
统计合法的时间数目并返回即可。
bool check(const char *time) {
int hh = atoi(time);
int mm = atoi(time + 3);
if (hh < 24 && mm < 60) {
return true;
} else {
return false;
}
}
void dfs(char *time, int pos, int *res) {
if (time[pos] == '\0') {
if (check(time)) {
(*res)++;
}
return;
}
if (time[pos] == '?') {
for (int i = 0; i <= 9; i++) {
time[pos] = '0' + i;
dfs(time, pos + 1, res);
time[pos] = '?';
}
} else {
dfs(time, pos + 1, res);
}
}
int countTime(char * time){
int res = 0;
dfs(time, 0, &res);
return res;
}