目录
1.网络延迟时间
弗洛伊德算法
迪杰斯特拉算法
2. K 站中转内最便宜的航班
3.从第一个节点出发到最后一个节点的受限路径数
4.到达目的地的方案数
1.网络延迟时间
有 n
个网络节点,标记为 1
到 n
。
给你一个列表 times
,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi)
,其中 ui
是源节点,vi
是目标节点, wi
是一个信号从源节点传递到目标节点的时间。
现在,从某个节点 K
发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1
。
示例 1:
输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2 输出:2
示例 2:
输入:times = [[1,2,1]], n = 2, k = 1 输出:1
示例 3:
输入: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)
对都 互不相同(即,不含重复边)
分析:要求最快能到达所有点,就是要找到这个点到达所有点的最短路径的最大值,用弗洛伊德算法的话就是比较暴力了,算出来所有的以后在需要的顶点找最大值,但是迪杰斯特拉算法少一层循环,只要这个顶点到记录到所有顶点的最小距离就好
弗洛伊德算法用邻接矩阵的时候,可以直接在邻接矩阵上操作,不用再利用结构体去建图
弗洛伊德算法
分析:要更新邻接矩阵,需要一个中间点,写在三层循环的最外层
#include <bits/stdc++.h>
using namespace std;
main()
{
//接受数据
int arc,n;
cin>>n>>arc;
int i,j,s[n][n];
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(j!=i) s[i][j]=99999;
else s[i][j]=0;
}
int a,b,c;
for(i=0; i<arc; i++)
{
cin>>a>>b>>c;
s[a-1][b-1]=c;
}
cin>>a;
a=a-1;
//三层循环
for(b=0; b<n; b++)
{
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
s[i][j]=min(s[i][j],s[i][b]+s[b][j]);
}
}
int ans=0;
for(i=0; i<n; i++)
ans=max(ans,s[a][i]);
// for(i=0; i<n; i++)
// {
// for(j=0; j<n; j++)
// cout<<s[i][j]<<" ";
// cout<<endl;
// }
ans=ans>9999?-1:ans;
cout<<ans;
}
迪杰斯特拉算法
分析:找到了一个讲迪杰斯特拉算法的文章,讲的挺详细的【C++】Dijkstra算法_dijkstra c++-CSDN博客
先从邻接矩阵开始写,要设置好visit[](做标记)和dist[](记录最短距离),从0开始循环n遍,保证每个点都可以做一次中间点,找到最短的且未被标记的,然后以她作为中间点更新
#include <bits/stdc++.h>
using namespace std;
main()
{
//接收数据
int arc,n;
cin>>n>>arc;
int i,j,k,s[n][n];
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(j!=i) s[i][j]=99999;
else s[i][j]=0;
}
int a,b,c;
for(i=0; i<arc; i++)
{
cin>>a>>b>>c;
s[a-1][b-1]=c;
}
for(i=0; i<n; i++)
{
for(j=0; j<n; j++)
{
cout<<s[i][j]<<" ";
}
cout<<endl;
}
cin>>a;
//迪杰斯特拉
int visit[n],dist[n];
memset(visit,0,sizeof(visit));
memset(dist,9999,sizeof(dist));
dist[a-1]=0;
dist[-1]=9999;
//循环n遍
for(i=0; i<n; i++)
{
j=-1;
//计算最小
for(k=0; k<n; k++)
{
if(!visit[k]&&dist[k]<=dist[j])
j=k;
}
//做标记
visit[j]=1;
//更新路径
for(k=0; k<n; k++)
{
dist[k]=min(dist[k],dist[j]+s[j][k]);
}
for(k=0;k<n;k++) cout<<dist[k]<<" ";
cout<<endl;
}
int ans=0;
for(i=0; i<n; i++)
{
ans=max(ans,dist[i]);
}
ans=ans>9999?-1:ans;
cout<<ans;
}
2. K 站中转内最便宜的航班
有 n
个城市通过一些航班连接。给你一个数组 flights
,其中 flights[i] = [fromi, toi, pricei]
,表示该航班都从城市 fromi
开始,以价格 pricei
抵达 toi
。
现在给定所有的城市和航班,以及出发城市 src
和目的地 dst
,你的任务是找到出一条最多经过 k
站中转的路线,使得从 src
到 dst
的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1
。
示例 1:
输入: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 1 输出: 200 解释: 城市航班图如下
从城市 0 到城市 2 在 1 站中转以内的最便宜价格是 200,如图中红色所示。
示例 2:
输入: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 0 输出: 500 解释: 城市航班图如下 从城市 0 到城市 2 在 0 站中转以内的最便宜价格是 500,如图中蓝色所示。
提示:
1 <= n <= 100
0 <= flights.length <= (n * (n - 1) / 2)
flights[i].length == 3
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 104
- 航班没有重复,且不存在自环
0 <= src, dst, k < n
src != dst
分析:我想到的时用迪杰斯特拉算法,在计算的过程中记录中转次数进行剪枝,比较容易出错的点就是可以中专k次,但是直接到达的算是中转0次,但是如果是原点和终点在同一个地方的话要比0次少一次,那就是-1次,但是我设置的是从原点到原点是0次,所以可以理解成到达某个地方需要乘坐几次航班,那就是乘坐k+1次时才算中转k次,这里比较容易出错
#include <bits/stdc++.h>
using namespace std;
main()
{
int arc,n;
cin>>n>>arc;
int i,j,k,s[n][n];
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(j!=i) s[i][j]=99999;
else s[i][j]=0;
}
int a,b,c;
for(i=0; i<arc; i++)
{
cin>>a>>b>>c;
s[a][b]=c;
}
// for(i=0; i<n; i++)
// {
// for(j=0; j<n; j++)
// {
// cout<<s[i][j]<<" ";
// }
// cout<<endl;
// }
cin>>a>>b>>c;
int visit[n],dist[n],ans[n];
memset(visit,0,sizeof(visit));
memset(dist,9999,sizeof(dist));
memset(ans,0,sizeof(ans));
dist[a]=0;
dist[-1]=9999;
for(i=0;i<n;i++)
{
j=-1;
for(k=0;k<n;k++)
{
if(!visit[k]&&dist[k]<=dist[j]) j=k;
}
visit[j]=1;
for(k=0;k<n;k++)
{
if(dist[k]>dist[j]+s[j][k] && ans[j]+1<=c+1)
{
ans[k]=ans[j]+1;
dist[k]=dist[j]+s[j][k];
}
}
}
// for(i=0;i<n;i++) cout<<ans[i]<<" ";
cout<<dist[b];
}
3.从第一个节点出发到最后一个节点的受限路径数
现有一个加权无向连通图。给你一个正整数 n
,表示图中有 n
个节点,并按从 1
到 n
给节点编号;另给你一个数组 edges
,其中每个 edges[i] = [ui, vi, weighti]
表示存在一条位于节点 ui
和 vi
之间的边,这条边的权重为 weighti
。
从节点 start
出发到节点 end
的路径是一个形如 [z0, z1, z2, ..., zk]
的节点序列,满足 z0 = start
、zk = end
且在所有符合 0 <= i <= k-1
的节点 zi
和 zi+1
之间存在一条边。
路径的距离定义为这条路径上所有边的权重总和。用 distanceToLastNode(x)
表示节点 n
和 x
之间路径的最短距离。受限路径 为满足 distanceToLastNode(zi) > distanceToLastNode(zi+1)
的一条路径,其中 0 <= i <= k-1
。
返回从节点 1
出发到节点 n
的 受限路径数 。由于数字可能很大,请返回对 109 + 7
取余 的结果。
示例 1:
输入:n = 5, edges = [[1,2,3],[1,3,3],[2,3,1],[1,4,2],[5,2,2],[3,5,1],[5,4,10]] 输出:3 解释:每个圆包含黑色的节点编号和蓝色的 distanceToLastNode 值。三条受限路径分别是: 1) 1 --> 2 --> 5 2) 1 --> 2 --> 3 --> 5 3) 1 --> 3 --> 5
示例 2:
输入:n = 7, edges = [[1,3,1],[4,1,2],[7,3,4],[2,5,3],[5,6,1],[6,7,2],[7,5,3],[2,6,4]] 输出:1 解释:每个圆包含黑色的节点编号和蓝色的 distanceToLastNode 值。唯一一条受限路径是:1 --> 3 --> 7 。
提示:
1 <= n <= 2 * 104
n - 1 <= edges.length <= 4 * 104
edges[i].length == 3
1 <= ui, vi <= n
ui != vi
1 <= weighti <= 105
- 任意两个节点之间至多存在一条边
- 任意两个节点之间至少存在一条路径
分析:这个是要先算出各个点到n点的最短路径,这个的话就应该是迪杰斯特拉算法了,然后放在dist[]中记录,然后就要搜索所有的路径根据dist[]的要求去剪枝,这种搜索的话我想到的是回溯,如果这条路可以走的话进行剪枝,但是我看题解好像更多人用的是动态规划 ,直接用dp[]表示1到i的受限路径数,这样会简单很多,这样放一起来想的话,回溯更适合那种要求写出路径的,像这种只求路径数的更适合动态规划
#include <bits/stdc++.h>
using namespace std;
main()
{
int arc,n;
cin>>n>>arc;
int i,j,k,s[n][n];
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(j!=i) s[i][j]=9999;
else s[i][j]=0;
}
int a,b,c;
for(i=0; i<arc; i++)
{
cin>>a>>b>>c;
s[a-1][b-1]=c;
s[b-1][a-1]=c;
}
// for(i=0; i<n; i++)
// {
// for(j=0; j<n; j++)
// {
// cout<<s[i][j]<<" ";
// }
// cout<<endl;
// }
int visit[n],dist[n],dp[n];
memset(visit,0,sizeof(visit));
memset(dist,9999,sizeof(dist));
memset(dp,0,sizeof(dp));
dist[n-1]=0;
dist[-1]=9999;
for(i=0;i<n;i++)
{
j=-1;
for(k=0;k<n;k++)
{
if(!visit[k]&&dist[k]<=dist[j]) j=k;
}
visit[j]=1;
for(k=0;k<n;k++)
{
dist[k]=min(dist[k],dist[j]+s[j][k]);
}
}
// for(i=0;i<n;i++) cout<<dist[i]<<" ";
cout<<endl;
dp[0]=1;
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
if(s[i][j]!=9999 && dist[i]>dist[j])
dp[j]=dp[j]+dp[i];
}
for(k=0;k<n;k++) cout<<dp[k]<<" ";
cout<<endl;
}
// for(i=0;i<n;i++) cout<<dp[i]<<" ";
cout<<endl;
cout<<dp[n-1];
//7 8
//1 3 1
//4 1 2
//7 3 4
//2 5 3
//5 6 1
//6 7 2
//7 5 3
//2 6 4
}
4.到达目的地的方案数
你在一个城市里,城市由 n
个路口组成,路口编号为 0
到 n - 1
,某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间最多有一条路。
给你一个整数 n
和二维整数数组 roads
,其中 roads[i] = [ui, vi, timei]
表示在路口 ui
和 vi
之间有一条需要花费 timei
时间才能通过的道路。你想知道花费 最少时间 从路口 0
出发到达路口 n - 1
的方案数。
请返回花费 最少时间 到达目的地的 路径数目 。由于答案可能很大,将结果对 109 + 7
取余 后返回。
示例 1:
输入:n = 7, roads = [[0,6,7],[0,1,2],[1,2,3],[1,3,3],[6,3,3],[3,5,1],[6,5,1],[2,5,1],[0,4,5],[4,6,2]] 输出:4 解释:从路口 0 出发到路口 6 花费的最少时间是 7 分钟。 四条花费 7 分钟的路径分别为: - 0 ➝ 6 - 0 ➝ 4 ➝ 6 - 0 ➝ 1 ➝ 2 ➝ 5 ➝ 6 - 0 ➝ 1 ➝ 3 ➝ 5 ➝ 6
示例 2:
输入:n = 2, roads = [[1,0,10]] 输出:1 解释:只有一条从路口 0 到路口 1 的路,花费 10 分钟。
提示:
1 <= n <= 200
n - 1 <= roads.length <= n * (n - 1) / 2
roads[i].length == 3
0 <= ui, vi <= n - 1
1 <= timei <= 109
ui != vi
- 任意两个路口之间至多有一条路。
- 从任意路口出发,你能够到达其他任意路口。
分析:我刚开始只想到了要用迪杰斯特拉算法计算出最小路径,然后再用动态规划计算路径数目,但是到计算路径数目的时候被难到了,总想着要用回溯去找路径,后来看了别人的题解才的发现有一个性质是:如果是合法路径,那么在途中经过的每一个点都是以最短路径的方式到达的,也就是说,走到这里消费的时间其实就是dist[i]
那么就可以用dp[i]记录走到这里等于dist[i]的数目,然后更新dp[i]
#include <bits/stdc++.h>
using namespace std;
main()
{
int arc,n;
cin>>n>>arc;
int i,j,k,s[n][n];
for(i=0; i<n; i++)
for(j=0; j<n; j++)
{
if(j!=i) s[i][j]=9999;
else s[i][j]=0;
}
int a,b,c;
for(i=0; i<arc; i++)
{
cin>>a>>b>>c;
s[a][b]=c;
s[b][a]=c;
}
// for(i=0; i<n; i++)
// {
// for(j=0; j<n; j++)
// {
// cout<<s[i][j]<<" ";
// }
// cout<<endl;
// }
int visit[n],dist[n],dp[n];
memset(visit,0,sizeof(visit));
memset(dist,9999,sizeof(dist));
memset(dp,0,sizeof(dp));
dist[0]=0;
dist[-1]=9999;
for(i=0;i<n;i++)
{
j=-1;
for(k=0;k<n;k++)
{
if(!visit[k]&&dist[k]<=dist[j]) j=k;
}
visit[j]=1;
for(k=0;k<n;k++)
{
dist[k]=min(dist[k],dist[j]+s[j][k]);
}
}
// for(i=0;i<n;i++) cout<<dist[i]<<" ";
cout<<endl;
dp[0]=1;
for(i=0;i<n;i++)
{
for(j=i+1;j<n;j++)
{
if(s[i][j]!=9999 && dist[j]==dist[i]+s[i][j])
dp[j]=dp[j]+dp[i];
}
// for(k=0;k<n;k++) cout<<dp[k]<<" ";
// cout<<endl;
}
// for(i=0;i<n;i++) cout<<dp[i]<<" ";
cout<<endl;
cout<<dp[n-1];
//7 10
//0 6 7
//0 1 2
//1 2 3
//1 3 3
//6 3 3
//3 5 1
//6 5 1
//2 5 1
//0 4 5
//4 6 2
}