DP----最短路问题Dijkstra算法入门与实战(C++)


方便自己预习也帮大佬们复习

概述

概念:
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。—《百度百科》

思想:
第一次遍历是从起点出发,找到最近的邻点位置,
在从第二次开始的后面的几次遍历中,都只考虑下一个要出发的最近的点并往其他点遍历并不断更新Dist

算法变量
Dist[i]表示从起点到i点的最短路总权
Map[i][j]表示从i点到j点的距离权

算法图示
无向图的储存(邻接矩阵)

0
1
2
3
4
5

转移后:

0 1 2 3 4 5
0 10 30 100
1 5
2 10 5 50
3 50 20 10
4 30 20 60
5 100 10 60

dijkstra模拟求最短路:

终点 Dist[i]
i=1 i=2 i=3 i=4 i=5
V1 INF INF INF INF INF
V2 10
V3 INF 60 50
V4 30 30
V5 100 100 90 60
Vj V2 V4 V3 V5

入门题

畅通工程续

HDUOJ测试链接

题目描述:
某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。
现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。

输入描述:
本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数N和M(0 < N < 200,0 < M < 1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0 <= A,B < N,A != B,0 < X < 10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0 <= S,T < N),分别代表起点和终点。

输出描述:
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.

样例:
输入
3 3
0 1 1
0 2 3
1 2 1
0 2
3 1
0 1 1
1 2
输出:
2
-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

思路:
1.每次输入先存Map[x][y]
2.先从起点扫一遍,能赋值Dist的就赋值了
3.从Dist[i]小到大一个个设置下一个出发点,并从当前出发点不断看看能不能更新别的Dist,每一个出发点只能出发一次
  每个出发点遍历时要注意的内容:这个点是否做过出发点、这个点往没用过的点走还有路吗,下一个该用哪个点出发
4.有通向结果的路? Dist[target]:-1;
#include <stack>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
typedef long long ll;
int Map[210][210];//Map[i][j]表示从i到j的原生距离
int Dist[210];//Dist[i]表示从起点到i的最近距离(不断更新)
int vis[210];//vis[i]记录是否从i出发过,1表还没,0表出发过了
int main()
{
    
    
    int N, M;
    while (cin >> N >> M)
    {
    
    
        rep1(i,0,210)//初始化,两个距离变量初始为最大值,记录变量为1
        {
    
    
            Dist[i] = INF, vis[i] = 1;
            rep1(j, 0, 210) Map[i][j] = INF;
        }

        int x, y, z;
        rep1(i,1,M)//对每个已知距离重定义map
        {
    
    
            cin >> x >> y >> z;
            Map[x][y] = min(Map[x][y], z);//小细节,防止一条路多次定义
            Map[y][x] = Map[x][y];//对称矩阵,来回都定
        }

        int start, target;//起点,终点
        cin >> start >> target;
        Dist[start] = 0;//起点到起点距离为0

        while (start != target)
        {
    
    
            vis[start] = 0;//每次的出发点都记录出发过了为0
            int Min = INF;//用来对比寻找下一个出发点的位置
            int Next;//表示下一个出发点的位置
            rep1(i,0,N-1)
            {
    
    
                if(!vis[i])//出发过的点不再对比,因为那个Dist[i]已经是那个位置的最短路了
                    continue;

                if(Map[start][i]!=INF)//若INF则表明中间没有路不能出发
                    Dist[i] = min(Dist[i], Dist[start] + Map[start][i]);//更新到i的最短路
                
                if(Dist[i]<Min)//在本次遍历,最小的就是下一次要出发的点
                    Min = Dist[i], Next = i;
            }
            if(Min==INF)//代表当前路是尽头
                break;
            start = Next;
        }

        //下面判断是否有到终点的路,该输出什么
        Dist[target] == INF ? cout << "-1" << endl:cout << Dist[target] << endl;
    }
    return 0;
}

简单题

Choose the best route

HDUOJ测试链接

题目描述:
有个人想从家去一个地方,只能乘坐公交车,有n个站牌,有个地图给出m个从站牌i到站牌j的坐车用时time。还有w个站牌是与家相邻的站牌。问到这个地方的最短用时。

输入描述:
多实例以文件结束停止
每个样例第一行三个整数n,m,s代表站牌数量,地图中有向路线的数量和这个人想去的地方
接下来m行每行三个整数x,y,z分别代表一条路线的始发站牌、结束站牌与中途用时
然后一个整数w代表与他家相邻的站牌数量
之后一行w个整数代表与他家相邻的站牌

输出描述:
若有到他想去的地方就输出最短用时,若没有输出-1

样例:
输入:
5 8 5
1 2 2
1 5 3
1 3 4
2 4 7
2 5 6
2 3 5
3 5 1
4 5 1
2
2 3
4 3 4
1 2 3
1 3 4
2 3 2
1
1
输出:
1
-1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

思路:
跟入门题一样,只不过变通一下,要自己设置起点
设置起点为0点,每一个与这个人家相邻的站牌到起点的距离都是0

这题就转化为了从0到输入的target的距离
#include <stack>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
typedef long long ll;
int map[1010][1010], dis[1010], vis[1010];

int main()
{
    
    
    int n, m, target, start;
    while (scanf("%d%d%d", &n, &m, &target) == 3)//终点有输入
    {
    
    
        start = 0;
        rep1(i,0,1010)//初始化
        {
    
    
            dis[i] = INF;
            vis[i] = 1;
            rep1(j, 0, 1010) map[i][j] = INF;
        }

        rep1(i,0,m-1)//修正map
        {
    
    
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            map[x][y] = min(map[x][y], z);//有向图,所以没有对称操作
        }

        int w;
        scanf("%d", &w);
        rep1(i,0,w-1)//插入无向源点0,并将所有相邻站牌与0的距离设置为0
        {
    
    
            int x;
            scanf("%d", &x);
            map[0][x] = map[x][0] = 0;
        }

        dis[start] = 0;
        while(start!=target)//正常dijstra算法
        {
    
    
            vis[start] = 0;
            int Min = INF;
            int next;
            rep1(i,1,n)
            {
    
    
                if(!vis[i])
                    continue;
                if(map[start][i]!=INF)
                    dis[i] = min(dis[i], dis[start] + map[start][i]);
                if(dis[i]<Min)
                    Min = dis[i], next = i;
            }
            if(Min==INF)
                break;
            start = next;
        }

        printf("%d\n", dis[target] == INF ? -1 : dis[target]);
    }
    return 0;
}

(这题没有弄明白的话请勿翻开下一题!)


一个人的旅行

HDUOJ测试链接

(这题是上一题的进阶版但可以说都一样,仔细想,检验上一题学会没)

题目描述:
虽然草儿是个路痴(就是在杭电待了一年多,居然还会在校园里迷路的人,汗~),但是草儿仍然很喜欢旅行,因为在旅途中 会遇见很多人(白马王子,0),很多事,还能丰富自己的阅历,还可以看美丽的风景……草儿想去很多地方,她想要去东京铁塔看夜景,去威尼斯看电影,去阳明山上看海芋,去纽约纯粹看雪景,去巴黎喝咖啡写信,去北京探望孟姜女……眼看寒假就快到了,这么一大段时间,可不能浪费啊,一定要给自己好好的放个假,可是也不能荒废了训练啊,所以草儿决定在要在最短的时间去一个自己想去的地方!因为草儿的家在一个小镇上,没有火车经过,所以她只能去邻近的城市坐火车(好可怜啊~)。

输入描述:
输入数据有多组,每组的第一行是三个整数T,S和D,表示有T条路,和草儿家相邻的城市的有S个,草儿想去的地方有D个;
接着有T行,每行有三个整数a,b,time,表示a,b城市之间的车程是time小时;(1=<(a,b)<=1000;a,b 之间可能有多条路)
接着的第T+1行有S个数,表示和草儿家相连的城市;
接着的第T+2行有D个数,表示草儿想去地方。

输出描述:
输出草儿能去某个喜欢的城市的最短时间。

样例:
输入:
6 2 3
1 3 5
1 4 7
2 8 12
3 8 4
4 9 12
9 10 2
1 2
8 9 10
输出:
9
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

思路:
跟入门题一样,只不过变通一下,要自己设置起点与终点
设置起点为0点,每一个与草儿家相邻的城市与起点的距离都是0
设置终点为1001点,每一个想去的地方与终点的距离都是0

这样问题就完美转换为从01001的最短路问题
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
typedef long long ll;
int map[1005][1005], dis[1005], vis[1005];
int main()
{
    
    
    int T, S, D;
    while (cin >> T >> S >> D)
    {
    
    
        rep1(i,0,1004)//初始化
        {
    
    
            dis[i] = INF, vis[i] = 1;
            rep1(j, 0, 1004) map[i][j] = INF;
        }

        rep1(i,0,T-1)//修正map
        {
    
    
            int x, y, z;
            cin >> x >> y >> z;
            map[x][y] = min(map[x][y], z);
            map[y][x] = map[x][y];
        }

        rep1(i,0,S-1)//临近点与起点的距离设为0
        {
    
    
            int x;
            cin >> x;
            map[0][x] = map[x][0] = 0;
        }
        rep1(i,0,D-1)//想去的地方与终点距离设为0
        {
    
    
            int x;
            cin >> x;
            map[x][1001] = map[1001][x] = 0;
        }

        int start = 0, target = 1001;//起点,终点
        dis[start] = 0;

        while(start!=target)//正常最短路操作
        {
    
    
            vis[start] = 0;
            int Min = INF;
            int next;
            rep1(i,0,1001)
            {
    
    
                if(!vis[i])
                    continue;
                if(map[start][i]!=INF)
                    dis[i] = min(dis[i], map[start][i] + dis[start]);
                if(dis[i]<Min)
                    Min = dis[i], next = i;
            }
            if(Min==INF)
                break;
            start = next;
        }
        printf("%d\n", dis[target] == INF ? -1 : dis[target]);
    }
    return 0;
}

散播谣言

POJ测试链接

题目描述:
众所周知,证券经纪业依靠的就是过度的传言。您需要想出股票经纪人中传播假情报的方法,让您的雇主在股票市场的占据优势。为了获得最大的效果,你必须蔓延最快的方式谣言。
不幸的是,股票经纪人信息只信任他们的“可靠来源”,这意味着你在你传播谣言之前必须考虑到他们的接触结构。它需要特定股票经纪人和一定的时间把谣言传递给他的每一位同事。你的任务将是写一个程序,告诉您选择哪一个股票经纪人作为谣言的出发点和所花费多少时间将谣言扩散到整个社会的股票经纪人。这一期限是衡量过去的人收到信息所需的时间。

输入描述:
你的程序包含多组股票经纪人的输入数据。每组以股票经纪人的人数开始。接下来的几行是每个经纪人与其他人接触的一些信息,包括这些人都是谁,以及将讯息传达到他们所需的时间。每个经纪人与其他人接触信息的格式如下:开头的第一个数表示共有n个联系人,接下来就有n对整数。每对整数列出的第一个数字指的是一个联系人(例如,一个’1’是指编号1的人),其次是在传递一个信息给那个人时所采取分钟的时间。没有特殊的标点符号或空格规则。
每个人的编号为1至经纪人数目。所花费的传递时间是从1到10分钟(含10分种)。股票经纪的人数范围是从1到100。当输入股票经纪人的人数为0时,程序终止。

输出描述:
在对于每一组数据,你的程序必须输出一行,包括的信息有传输速度最快的人,以及在最后一个人收到消息后,所总共使用的时间(整数分钟计算)。
你的程序可能会收到的一些关系会排除一些人,也就是有些人可能无法访问。如果你的程序检测到这样一个破碎的网络,只需输出消息“disjoint”。请注意,所花费的时间是从A传递消息到B,B传递信息到A不一定是花费同样的传递时间,但此类传播也是可能的。

样例:
输入
3
2 2 4 3 5
2 1 2 3 6
2 1 2 2 2
5
3 4 4 2 8 5 3
1 5 8
4 1 6 4 10 2 7 5 2
0
2 2 5 1 5
0
输出
3 2
3 10

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

这道题难在题意理解与逻辑分析上面,
只要分析到同事之间是以群散步的,
每个人向外传谣只需要取他对每个人传谣用时的最大值即可
(理解难了...我扣了三个小时)
1.枚举造谣者与最后受谣人,寻找这个人散播谣言的最大用时
  (如果他有传不到的人,那么这条途径自然就成了INF)
2.在每个造谣人之间选取最小用时的人就行了
#include <stack>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <cstdio>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
typedef long long ll;
int map[110][110], vis[110];
int n;
void init()//初始化与打印地图
{
    
    
    rep1(i, 1, n)
        rep1(j, 1, n)
            map[i][j] = INF;
    rep1(i, 1, n)
    {
    
    
        int nFriends;
        scanf("%d", &nFriends);
        while (nFriends--)
        {
    
    
            int x, s;
            scanf("%d%d", &x, &s);
            map[i][x] = min(map[i][x], s);
        }
    }
}
int dijkstra(int start,int target)//普通dijstra
{
    
    
    int dis[110];
    rep1(i, 0, 109) dis[i] = INF;
    mm(vis, 0);
    dis[start] = 0;
    while(start!=target)
    {
    
    
        int Min = INF;
        int next;
        vis[start] = 1;
        rep1(i,1,n)
        {
    
    
            if(vis[i])
                continue;
            if(map[start][i]!=INF)
                dis[i] = min(dis[i], dis[start] + map[start][i]);
            if(Min>dis[i])
                Min = dis[i], next = i;
        }
        if(Min==INF)
            break;
        start = next;
    }
    return dis[target];
}

int main()
{
    
    
    while (~scanf("%d", &n), n)
    {
    
    
        init();

        int Min = INF;
        int flagI;
        rep1(i,1,n)//枚举起点与终点
        {
    
    
            int Max = 0;//记录以i为起点的向各个位置散布用时的最大值(即i向所有人散拨谣言的用时)
            rep1(j,1,n)
            {
    
    
                if(i==j)//因为map[i][i]我们还是INF,所以要跳过去
                    continue;
                Max = max(Max, dijkstra(i, j));
            }
            cout << Max << endl;
            if(Min>Max)//记录所有人中散播完谣言用时的最小值
                Min = Max, flagI = i;
        }
        Min == INF ? printf("disjoint\n") : printf("%d %d\n", flagI, Min);
    }
    return 0;
}

HDU Today

HDU测试链接

题目描述:
经过锦囊相助,海东集团终于度过了危机,从此,HDU的发展就一直顺风顺水,到了2050年,集团已经相当规模了,据说进入了钱江肉丝经济开发区500强。这时候,XHD夫妇也退居了二线,并在风景秀美的诸暨市浬浦镇陶姚村买了个房子,开始安度晚年了。
这样住了一段时间,徐总对当地的交通还是不太了解。有时很郁闷,想去一个地方又不知道应该乘什么公交车,在什么地方转车,在什么地方下车(其实徐总自己有车,却一定要与民同乐,这就是徐总的性格)。
徐总经常会问蹩脚的英文问路:“Can you help me?”。看着他那迷茫而又无助的眼神,热心的你能帮帮他吗?
请帮助他用最短的时间到达目的地(假设每一路公交车都只在起点站和终点站停,而且随时都会开)。

输入描述:
输入数据有多组,每组的第一行是公交车的总数N(0<=N<=10000);
第二行有徐总的所在地start,他的目的地end;
接着有n行,每行有站名s,站名e,以及从s到e的时间整数t(0<t<100)(每个地名是一个长度不超过30的字符串)。
note:一组数据中地名数不会超过150个。
如果N==-1,表示输入结束。

输出描述:
如果徐总能到达目的地,输出最短的时间;否则,输出“-1”。

样例:
输入
6
xiasha westlake
xiasha station 60
xiasha ShoppingCenterofHangZhou 30
station westlake 20
ShoppingCenterofHangZhou supermarket 10
xiasha supermarket 50
supermarket westlake 10
-1
输出:
50
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

本题与入门题思路一样,注意坑就行
博主先是WA半天又是string输入TLE半天..................
#include <stack>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <map>
#include <unordered_map>
#include <set>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
typedef long long ll;
unordered_map<string, int> pos;//记录城镇编号
int Map[160][160];
int dis[160];
int vis[160];
int n;
int cnt;
void init()//初始化
{
    
    
    pos.clear();
    rep1(i,0,159)
    {
    
    
        dis[i] = INF;
        vis[i] = 0;
        rep1(j, 0, 159)
            Map[i][j] = INF;
    }
}

int dijkstra(int start,int target)//普通的dijkstra
{
    
    
    dis[start] = 0;
    while(start!=target)
    {
    
    
        vis[start] = 1;
        int Min = INF;
        int next;
        rep1(i,1,cnt)
        {
    
    
            if(vis[i])
                continue;
            if(Map[start][i]!=INF)
                dis[i] = min(dis[i], dis[start] + Map[start][i]);
            if(Min>dis[i])
                Min = dis[i], next = i;
        }
        if(Min==INF)
            break;
        start = next;
    }
    return (dis[target] == INF ? -1 : dis[target]);
}

int main()
{
    
    
    while(scanf("%d",&n)!=EOF,n!=-1)
    {
    
    
        cnt = 0;
        init();
        char x[40], y[40];
        scanf("%s %s ", x, y);
        pos[x] = ++cnt;
        if(!pos[y])//注意起点与终点也可能相同,碰上有的编号就加一
        pos[y] = ++cnt;
        int start = pos[x], target = pos[y];
        rep1(i,1,n)
        {
    
    
            int s;
            scanf("%s %s %d", x, y, &s);
            if(!pos[x])
                pos[x] = ++cnt;
            if(!pos[y])
                pos[y] = ++cnt;
            Map[pos[x]][pos[y]] = min(Map[pos[x]][pos[y]], s);//正常打印地图
            Map[pos[y]][pos[x]] = Map[pos[x]][pos[y]];
        }
        cout << dijkstra(start, target) << endl;
    }
    return 0;
}


网络延迟时间

LeetCode测试链接

题目描述:
有 n 个网络节点,标记为 1 到 n。
给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。
现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。

样例:
输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
输出:2
输入:times = [[1,2,1]], n = 2, k = 1
输出:1
输入:times = [[1,2,1]], n = 2, k = 2
输出:-1

数据范围:
1 <= k <= n <= 100
1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
所有 (ui, vi) 对都 互不相同(即,不含重复边)
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

《散播谣言》的简化版,只需要枚举终点并统计最大值即可,有疑惑翻上面那题即可
class Solution {
    
    
public:
const int INF=0x7FFFFFFF;
int map[110][110],dis[110],vis[110];
    int networkDelayTime(vector<vector<int>>& times, int n, int k) {
    
    
        
        //初始化地图
        for(int i=0;i<110;i++) for(int j=0;j<110;j++) map[i][j]=INF;
        
        //绘制地图
        for(int i=0;i<times.size();i++)
            map[times[i][0]][times[i][1]]=min(map[times[i][0]][times[i][1]],times[i][2]);
        
        int Max=0;//统计最大值

        //Dijkstra
        for(int i=1;i<=n;i++)//枚举Target
        {
    
    
            for(int j=0;j<110;j++) dis[j]=INF,vis[j]=0;//初始最短路变量
            
            int start=k,target=i;//终点设置为枚举的i
            
            dis[start]=0;
            while(start!=target)
            {
    
    
                vis[start]=1;
                int Min=INF;
                int next;
                for(int j=1;j<=n;j++)
                {
    
    
                    if(vis[j]) continue;
                    if(map[start][j]!=INF) dis[j]=min(dis[j],dis[start]+map[start][j]);
                    if(Min>dis[j]) Min=dis[j],next=j;
                }
                if(Min==INF) break;
                start=next;
            }
            Max=max(Max,dis[target]);
        }
        return (Max==INF? -1:Max);//若Max=INF说明有dis[i]为INF说明无法全达到
    }
};

*概念题

记录路径

POJ测试链接

题目描述:
多实例,以文件结束停止读入
每个实例给出n条边与终点target,接下来n行每行两个整数a.b代表点a与点b相连,问从1到target最少要经过几个点,并记录输出这些点;如果没有能到达的方式,输出-1

样例:
输入:
6 5
1 3
3 2
2 3
3 1
2 5
5 4
输出:
4
1
3
2
5

数据范围:
1 <= n <= 50,000
1 <= target <= 1,000
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

//*****先思考dijkstra的过程都是什么意思
思路:
在普通dijkstra中
将dis[i]=min(dis[i],dis[start]+map[start][i])改为
  if (dis[i] > dis[start] + map[start][i])//记录路径
  {
    
    
       dis[i] = dis[start] + map[start][i];
       path[i] = start;//加了个记录i点的前驱是start点
  }
然后在输出路径时采用倒序记录输出或者dfs输出就行
#include <stack>
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
#include <cstring>
#include <cmath>
#include <cstdio>
#define rep1(i, a, n) for (int i = a; i <= n; i++)
#define rep2(i, a, n) for (int i = a; i >= n; i--)
#define mm(a, b) memset(a, b, sizeof(a))
#define elif else if
typedef long long ll;
const int INF = 0x7FFFFFFF;
using namespace std;
int n, k;
int map[1010][1010];//map[i][j]表示从i到j的距离
int dis[50010];//从起点到目标位置i的最短路
int vis[50010];//is[i]记录是否用过i这个起点
int path[50010];//记录前驱点
int way[50010];//用来倒序输出的路径
void paintMap()//绘制地图
{
    
    
    rep1(i, 0, 1009) rep1(j, 0, 1009) map[i][j] = INF;
    rep1(i, 1, n)
    {
    
    
        int x, y;
        scanf("%d%d", &x, &y);
        map[x][y] = 1;
    }
}
void init()//初始化
{
    
    
    rep1(i, 0, 50009) dis[i] = INF, vis[i] = 0;
    rep1(i, 0, 50009) way[i] = -1, path[i] = -1;
}
void drawWay(int beg, int en)//输出路径
{
    
    
    int cnt = 0;
    way[++cnt] = en;//最后一个保存上
    while (beg != en)
    {
    
    
        way[++cnt] = path[en];
        en = path[en];//与并查集类似,不断向上搜索这个点的老大是谁
    }
    rep2(i, cnt, 1)//倒序记录,倒序输出
    {
    
    
         printf("%d\n", way[i]);
    }
}
void dijkstra(int start, int target)//普通dijkstra算法模板
{
    
    
    int beg = start;
    int en = target;
    init();
    dis[start] = 0;
    while (start != target)
    {
    
    
        int Min = INF;//用来判断下一个最小的dis[i]起点i是哪个
        int next;//下一个起点
        vis[start] = 1;
        rep1(i, 1, k)
        {
    
    
            if (vis[i])
                continue;
            if (map[start][i] != INF)
            {
    
    
                if (dis[i] > dis[start] + map[start][i])//记录路径
                {
    
    
                    dis[i] = dis[start] + map[start][i];
                    path[i] = start;//加了个记录i点的前驱是start点
                }
            }
            if (Min > dis[i])
                next = i, Min = dis[i];
        }
        if (Min == INF)
            break;
        start = next;
    }
    if (dis[target] == INF)//走不到target
    {
    
    
        printf("-1\n");
        return;
    }
    printf("%d\n", dis[target] + 1);
    drawWay(beg, en);
}
int main()
{
    
    
    while (scanf("%d%d", &n, &k)==2)
    {
    
    
        paintMap();
        dijkstra(1, k);
    }
    return 0;
}

思维题

单词接龙

LeetCode测试链接

题目描述:
字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列:
序列中第一个单词是 beginWord 。
序列中最后一个单词是 endWord 。
每次转换只能改变一个字母。
转换过程中的中间单词必须是字典 wordList 中的单词。
给你两个单词 beginWord 和 endWord 和一个字典 wordList ,找到从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列,返回 0。

样例:
输入:
beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”,“cog”]
输出:
5
解释:
一个最短转换序列是 “hit” -> “hot” -> “dot” -> “dog” -> “cog”, 返回它的长度 5。
输入:
beginWord = “hit”, endWord = “cog”, wordList = [“hot”,“dot”,“dog”,“lot”,“log”]
输出:
0
解释:
endWord “cog” 不在字典中,所以无法进行转换。

数据:
1 <= beginWord.length <= 10
endWord.length = beginWord.length
1 <= wordList.length <= 5000
wordList[i].length = beginWord.length
beginWord、endWord 和 wordList[i] 由小写英文字母组成
beginWord != endWord
wordList 中的所有字符串 互不相同

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

//作为一道困难题而我又想用dijkstra写,成功地卡了我四个小时
思路:
1.从选取最少字符过程数可以看出,可以用最短路
2.最短路中的遍历枚举目标点我们可以转换为枚举目标单词
3.我们在枚举在每一次的出发点延申出来的目标单词时,用bfs思想队列思想:
  如果枚举到的单词在库内且未被赋值时,赋值为dis[start]+1
class Solution {
    
    
public:
    int ladderLength(string start, string target, vector<string>& wordList) {
    
    
        
        unordered_set<string> wordSet(wordList.begin(), wordList.end());//方便查找可选字符串中是否存在

        //特殊情况
        if (!wordSet.count(target)) return 0;
        if(!wordSet.count(target)) return 0;

        unordered_map<string, int> dis;//存放从start到一个字符串的距离
        dis[start]=1;//初始化为1,因为要求非路径而是顶点数
        
        queue<string> cur;//为每一次的start提供可选空间(防止选错)
        cur.push(start);

        while (!cur.empty()) 
        {
    
    
            start = cur.front(); 
            if(start==target) break;//dijkstra中的while(start!=target)变形
            cur.pop();

            for (int i = 0; i < start.size(); i++) //枚举每个位置上从a-z置换后的情况
            {
    
    
                string toChange = start;
                for (int j = 0; j < 26; j++) 
                {
    
    
                    toChange[i] = j+'a';

                    if(!wordSet.count(toChange)) continue;//dijkstra中的map[i][j]==INF变形
                    if (!dis.count(toChange)) //因为初始为0,有bfs的思想:先入为主
                    {
    
    
                        cur.push(toChange);//可以作为start的选项,放进去
                        dis[toChange] = dis[start] + 1;
                    }   
                }
            }
        }
        return dis[target];
    }
};

猜你喜欢

转载自blog.csdn.net/SnopzYz/article/details/113754911
今日推荐