前言
这篇博客本一直处于咕咕咕的状态,但由于某人最短路的突击检查并不理想,因此写一篇博客进行细致(才怪)的总结。
Floyd
什么是Floyd呢?问问度娘吧
Floyd 和 区间DP 有点像,动态规划点集大小为区间。
在突测中,本人非常zz的把k循环打错位置了,却忽略了dp[i][j][k]表示的是由i到j途径<= k 的点时的最短路
代码
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
using namespace std;
int n, m;
int dp[105][105];
int pre[105][105];
int main() {
scanf("%d %d", &n, &m);
int x = 0, y = 0;
memset(dp, 0x3f, sizeof(dp));
for(int i = 1; i<= n; i ++){
dp[i][i] = 0;
}
int m;
for(int i = 1; i <= m; i ++){
int x, y;
scanf("%d %d", &x, &y);
dp[x][y] = 1;
}
for(int k = 1; k <= n; k ++){
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
if((dp[i][k] + dp[k][j]) < dp[i][j]){
dp[i][j] = dp[i][k] + dp[k][j];
}
}
}
}
int s, t;
scanf("%d %d", &s, &t);
if(dp[s][t] = 0x3f){
printf("no solution
");
}
else{
printf("%d", dp[s][t]);
}
return 0;
}
题目描述
有 n 个 城市,从1到n给他们编号,它们之间由一些单向道路(即一条道路只能从一个方向走向另一个方向,反之不行)相连,每条路还有一个花费c(i),表示通过第i条边需要花费c(i)的时间。
求任意两点间的最快路径
输入格式
第一行一个整数,表示有多少个城市和多少条道路。
接下来n行,每行n个整数
第i + 1行第j个数表示从到有一条花费为的边。(第行第个数为0)
输出格式
n行,每行n个整数
第i行第j个数表示从到最少需要多少时间。(第行第个数为0)
样例
输入样例
4
0 487 569 408
705 0 306 357
95 222 0 618
961 401 688 0
输出数据
0 487 569 408
401 0 306 357
95 222 0 503
783 401 688 0
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 505;
int n;
int dp[maxn][maxn];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
dp[i][j] = 0x3f3f3f3f;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
scanf("%d", &dp[i][j]);
}
}
for (int k = 1; k <= n; k++) {
for (int i = 1; i <= n; i++) {
int j = i + k - 1;
for (int j = 1; j <= n; j++) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
printf("%d ", dp[i][j]);
}
printf("
");
}
return 0;
}
从上述代码中我们就可以显而易见的发现,Floyd有一个致命的弱点就是它的时间复杂度是 (O(n^3)) 但是它也有很多优点呀!比如它可以实现负权的情况,同时它可以多源点查询。
但我们要输出最短路径该怎么做呢?
用递归实现就好了,每一位保存其对应的前一位的位置
#include <cstdio>
#include <cstring>
using namespace std;
int n, m;
int dp[505][505];
int pre[505][505];
int flag1, flag2;
void print(int x){
if(pre[flag1][x] == 0){
return;
}
print(pre[flag1][x]);
printf(" %d", x);
}
int main() {
memset(dp, 0x3f, sizeof(dp));
scanf("%d %d", &n, &m);
for(int i = 1; i <= n; i ++){
dp[i][i] = 0;
}
for(int i = 1; i <= m; i ++){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
dp[a][b] = c;
pre[a][b] = a;
}
for(int k = 1; k <= n; k ++){
for(int i = 1; i <= n; i ++){
for(int j = 1; j <= n; j ++){
if(dp[i][j] > dp[i][k] + dp[k][j]){
dp[i][j] = dp[i][k] + dp[k][j];
pre[i][j] = pre[k][j];
}
}
}
}
scanf("%d %d", &flag1, &flag2);
printf("%d
", dp[flag1][flag2]);
printf("%d", flag1);
print(flag2);
return 0;
}
Dijkstra
思想
运用贪心的思想,不断的找出未被标记且离s最近的点,并运用松弛的思想,改变运用该点松弛的值,在遍历完成后,输出结果。
证明(非严格,不详细)
因为每一个输出时, 它已经成为了未被标记且离s最近的点,在剩下的其余点中,无论如何都不可能短于它,也就不可能构成其的最短路,而在前面已被输出的数中能连接的都进行了判断,不能连接的。。。
所以当每个点都被标注时都成为了其最短路。(我哔哔了些啥???)
堆优化版本代码(我的代码是真的丑)
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
const int maxn = 2505;
struct node{
int v, w;
};
struct data{
int p, vis;
bool operator < (const data x) const {return vis > x.vis;}
};
int n, m, s, t;
int vis[maxn];
int v[maxn];
vector<node> way[maxn];
priority_queue<data> q;
void dijkstra(int s, int t){
memset(vis, 0x3f3f3f3f, sizeof(vis));
memset(v, 0, sizeof(v));
vis[s] = 0;
data flag2;
flag2.p = s;
flag2.vis = 0;
q.push(flag2);
while(!q.empty()){
data flag3;
flag3 = q.top(); // flag3 为当前最小值
q.pop();
if(v[flag3.p] == 1)continue;
v[flag3.p] = 1;
for(int i = 0; i < way[flag3.p].size(); i ++){
node flag4;
flag4 = way[flag3.p][i];
if(vis[flag4.v] > way[flag3.p][i].w + vis[flag3.p]){
vis[flag4.v] = way[flag3.p][i].w + vis[flag3.p];
data flag5;
flag5.p = flag4.v;
flag5.vis = vis[flag4.v];
q.push(flag5);
}
}
}
}
int main() {
scanf("%d %d %d %d", &n, &m, &s, &t);
for(int i = 1; i <= m; i ++){
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
node flag;
flag.v = b;
flag.w = c;
way[a].push_back(flag);
flag.v = a;
way[b].push_back(flag);
}
dijkstra(s, t);
printf("%d", vis[t]);
return 0;
}
之后应该还会有Bellman_fort, spfa的博客
但