题目描述
小明喜欢观景,于是今天他来到了公园。
已知公园有 N 个景点,景点和景点之间一共有 M 条道路。小明有 Q 个观景计划,每个计划包含一个起点 st 和一个终点 ed,表示他想从 st 去到 ed。但是小明的体力有限,对于每个计划他想走最少的路完成,你可以帮帮他吗?
输入描述
输入第一行包含三个正整数 N,M,Q
第 2 到 M + 1 行每行包含三个正整数 u,v,w 表示 u↔v 之间存在一条距离为 w 的路。
第 M+2 到 M + Q-1 行每行包含两个正整数 st,ed,其含义如题所述。
(1 ≤ N ≤ 400,1 ≤ M ≤ N×(N−1)/2,Q ≤ 10^3,1 ≤ u,v,st,ed ≤ n,1 ≤ w ≤ 10^9)
输出描述
输出共 Q 行,对应输入数据中的查询。
若无法从 st 到达 ed 则输出 -1。
样例输入
3 3 3
1 2 1
1 3 5
2 3 2
1 2
1 3
2 3
样例输出
1
3
2
参考代码:
#include
using namespace std;
const long long INF = 0x3f3f3f3f3f3f3f3fLL; //这样定义INF的好处是: INF <= INF+x
const int N = 405;
long long dp[N][N];
int n,m,q;
void input(){
//两种初始化方法
// for(int i = 1; i <= n; i++)
// for(int j = 1; j <= n; j++)
// dp[i][j] = INF;
memset(dp,0x3f,sizeof(dp));
for(int i = 1; i <= m; i++){
int u,v;long long w;
cin >> u >> v >> w;
dp[u][v]=dp[v][u] = min(dp[u][v] , w); //防止有重边
}
}
void floyd(){
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
dp[i][j] = min(dp[i][j] , dp[i][k] + dp[k][j]);
}
void output(){
int s, t;
while(q--){
cin >> s >>t;
if(dp[s][t]==INF) cout << "-1" <> n >> m >> q;
input();
floyd();
output();
return 0;
}
题目描述
王国有 N 个城市,任意两城市间有直通的路或没有路。每条路有过路费,并且经过每个城市都要交税。定义从 a 城到 b 城,其花费为路径长度之和,再加上除 a 与 b 外所有城市的过路费之和。
现给定若干对城市,请你打印它们之间最小花费的路径。如果有多条路经符合,则输出字典序最小的路径。
输入描述
第一行给定一个 N 表示城市的数量,若 N=0 表示结束。
接下来 N 行,第 i 行有 N 个数, , 表示第 i 个城市到第 j 个城市的直通路过路费,若 = −1 表示没有直通路。
接下来一行有 N 个数,第 i 个数表示第 i 个城市的税。再后面有很多行,每行有两个数,表示起点和终点城市,若两个数是 -1,结束。
输出描述
对给定的每两个城市,输出最便宜的路径经过哪些点,以及最少费用。
样例输入
3
0 2 -1
2 0 5
-1 5 0
1 2 3
1 3
2 3
-1 -1
0
样例输出
From 1 to 3 :
Path: 1-->2-->3
Total cost : 9
From 2 to 3 :
Path: 2-->3
Total cost : 5
参考代码:
路径部分说明如下:
1.路径的定义。用 path[][] 记录路径,path[i][j] = u 表示起点为 i,终点为 j 的最短路径,从 i 出发下一个点是 u。一条完整的路径,是从 s 出发,查 path[s][j] = u 找到下一个点 u,然后从 u 出发,查 path[u][j] = v,下一个点是 v,等等,最后到达终点 j。2.路径的计算。path[i][j] = path[i][k] 计算了从 i 出发的下一个点。因为 k 在 i-j 的最短路径上,所以 i-k 也是最短路径。比较 path[i][j] 和 path[i][k],它们都表示从 i 出发的下一个点,且这个点在同一个路径上,所以有 path[i][j] = path[i][k]。
#include
using namespace std;
const int INF = 0x3fffffff;
const int N = 505;
int n, mmap[N][N], tax[N], path[N][N];
void input(){
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) {
scanf("%d", &mmap[i][j]);
if(mmap[i][j] == -1) mmap[i][j] = INF;
path[i][j] = j; //path[i][j]: 此时i、j相邻,或者断开
}
for(int i = 1; i <= n; i++) scanf("%d", &tax[i]); //税
}
void floyd(){
for(int k = 1; k <= n; k++)
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) {
int len = mmap[i][k] + mmap[k][j] + tax[k]; //计算最短路
if(mmap[i][j] > len) {
mmap[i][j] = len;
path[i][j] = path[i][k]; //标记到该点的前一个点
}
else if(len == mmap[i][j] && path[i][j] > path[i][k])
path[i][j] = path[i][k]; //若距离相同,按字典序
}
}
void output(){
int s, t;
while(scanf("%d %d", &s, &t)) {
if(s == -1 && t == -1) break;
printf("From %d to %d :\n", s, t);
printf("Path: %d", s);
int k = s;
while(k != t) { //输出路径从起点直至终点
printf("-->%d", path[k][t]);
k = path[k][t]; //一步一步往终点走
}
printf("\n");
printf("Total cost : %d\n\n", mmap[s][t]);
}
}
int main(){
scanf("%d", &n);
input();
floyd();
output();
return 0;
}
题目描述
一个图有 n 个点,有 m 个边连接这些点,边长都是 1 千米。小明的移动能力很奇怪,他一秒能跑 2^t 千米,t 是任意自然整数。问小明从点 1 到点 n,最少需要几秒。
输入描述
第一行两个整数 n,m,表示点的个数和边的个数。
接下来 m 行每行两个数字 a,b,表示一条 a 到 b 的边。
(1 ≤ n ≤ 50,1 ≤ m ≤ 10000,最优路径长度 ≤ 2^32。)
输出描述
输出一个整数,表示答案。
样例输入
5 5
1 2
2 2
3 4
3 5
2 3
样例输出
1
参考代码:
这道题的路径在 “兜圈子”,某些点可以经过多次。
小明所跑的路径,可以分成几段,每一段长为 2^t,所以关键在于确定任意点对 (i, j) 点之间是否存在 2^t 的路径。由于要计算所有点对之间的路径,所以用 Floyd 算法是合适的。计算出一个新图,若点对 (i, j) 之间的存在长 2^t 的路径,把 (i, j) 的边长 mp[i][j] 赋值为 1 秒,否则为无穷大。在这个新图上,求 1 到 n 的最短路径就是答案。
计算长度为 2^t 的路径:可以根据倍增的原理,有 2^t = 2^{t-1} +2^{t-1}。用 p[i][j][t] = true 表示 i 、 j 之间有一条长 2^t 的路径,根据 Floyd 算法的思路,路径通过一个中转点 k,有 p[i][j][t]= p[i][k][t-1]+ p[k][j][t-1]。利用倍增原理计算新图 mp[],复杂度 O(n^3)。在新图 mp[] 上计算最短路,用任何最短路算法都行,这里就用最简单的 Floyd 算法。
#include
using namespace std;
const int N = 55;
bool p[N][N][34];
int mp[N][N];
int main(){
memset(mp,0x3f,sizeof(mp));
int n,m; cin >> n >> m;
for( int i = 1;i <= m; i++){
int u,v; cin >> u >> v;
mp[u][v] = 1;
p[u][v][0] = true;
}
for(int t = 1;t <= 32; t++) //长度为2^t的路径
for(int k = 1;k <= n; k++) //Floyd
for(int i = 1;i <= n; i++)
for(int j = 1;j <= n; j++)
if(p[i][k][t - 1] == true && p[k][j][t - 1] == true){
p[i][j][t] = true;
mp[i][j] = 1; //计算得到新图
}
for(int k = 1;k <= n; k++) //求最短路,就用Floyd
for(int i = 1;i <= n; i++)
for(int j = 1;j <= n; j++)
mp[i][j] = min(mp[i][j],mp[i][k] + mp[k][j]);
cout << mp[1][n] << endl;
return 0;
}
如有错误和需要改进完善之处,欢迎大家纠正指教。