奶牛跑步
Description
Bessie準備用從牛棚跑到池塘的方法來鍛鍊. 但是因爲她懶,她只准備沿着下坡的路跑到池塘,然後走回牛棚.
Bessie也不想跑得太遠,所以她想走最短的路經. 農場上一共有M(1<=M<=10,000)條路,每條路連接兩個用1..N(1<=N<=1000)標號的地點. 更方便的是,如果X>Y,則地點X的高度大於地點Y的高度. 地點N是Bessie的牛棚;地點1是池塘.
很快, Bessie厭倦了一直走同一條路.所以她想走不同的路,更明確地講,她想找出K(1<=K<=100)條不同的路經.爲了避免過度勞累,她想使這K條路徑爲最短的K條路徑.
請幫助Bessie找出這K條最短路經的長度.你的程序需要讀入農場的地圖, 一些從Xi到Yi的路徑和它們的長度(Xi,Yi,Di).
所有(Xi,Yi,Di) 滿足( 1<=Yi<Xi; Yi<Xi<=N, 1<=Di<=1,000,000 ).
Input
第1行: 3個數: N,M,K
第2..M+1行: 第 i+1行包含3個數 Xi,Yi,Di, 表示一條下坡的路.
Output
第1..K行: 第i行包含第i最短路徑的長度,或−1如果這樣的路徑不存在.如果多條路徑有同樣的長度,請注意將這些長度逐一列出.
Sample Input
5 8 7
5 4 1
5 3 1
5 2 1
5 1 1
4 3 4
3 1 1
3 2 1
2 1 1
Sample Output
1
2
2
3
6
7
-1
Hint
【樣例解釋】
路徑分別爲(5−1),(5−3−1),(5−2−1),(5−3−2−1),(5−4−3−1),(5−4−3−2−1)
Source
動態規劃, 圖論, A*搜索, k短路
Solution
這道題的標程是A*搜索,然而在網上看到了一個跑的飛快的神奇的遞推,於是妥妥的放棄了A*……
對與插入的每一個點(0號~n - 1號)我們可以記錄一個數組h[i][j],表示i號點到n - 1號點的第j小路的長度,那麼最後答案存於h[0][i]中,遞推方程顯然有:
for (int i = 0; i < k; ++i)//更新第i小的邊
if (h[x][t1] + d < h[y][t2]) temp[i] = h[x][t1++] + d;//用其父邊更新最短距離(先到父親再從父親去n - 1)
else temp[i] = h[y][t2++];//用自己更新(直接從自己去n - 1)
for (int i = 0; i < k; ++i) h[y][i] = temp[i];//記錄第i小路徑
對於初始化,除了從n - 1到n - 1的第1小邊爲0外,其他值都初始化爲最大值(即不存在)
Code
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <stack>
#include <map>
#include <vector>
#include <queue>
#define LL long long
using namespace std;
inline int gi() {
char cj = getchar();
int ans = 0, f = 1;
while (cj < '0' || cj > '9') {
if (cj == '-') f = -1;cj = getchar();
}
while (cj >= '0' && cj <= '9') ans = ans * 10 + cj - '0', cj = getchar();
return f * ans;
}
struct node {
int to, nxt, w;
} e[10010];
int n, m, k, a, b, c, head[1010], cnt, h[1010][110], temp[110];
inline void add(int a, int b, int c) {
e[++cnt].nxt = head[a], e[cnt].to = b, e[cnt].w = c, head[a] = cnt;
}
inline void update(int x, int y, int d) {
int t1 = 0, t2 = 0;
for (int i = 0; i < k; ++i)
if (h[x][t1] + d < h[y][t2]) temp[i] = h[x][t1++] + d;
else temp[i] = h[y][t2++];
for (int i = 0; i < k; ++i) h[y][i] = temp[i];
}
int main() {
freopen("cowjog.in", "r", stdin);
freopen("cowjog.out", "w", stdout);
n = gi(), m = gi(), k = gi();
for (int i = 0; i < m; ++i) {
a = gi(), b = gi(), c = gi();
add(a - 1, b - 1, c);
}
for (int i = 0; i < n; ++i)
for (int j = 0; j < k; ++j)
h[i][j] = 1e9; h[n - 1][0] = 0;
for (int i = n - 1; i >= 0; --i)
for (int j = head[i]; j; j = e[j].nxt)
update(i, e[j].to, e[j].w);
for (int i = 0; i < k; ++i)
printf("%d\n", h[0][i] == 1e9 ? -1 : h[0][i]);
return 0;
}
Summary
考試的時候完全不會寫(之前沒有練過A*搜索),就直接輸出-1,果然沒有一個這種點……
注意遞推的時候每使用一次h[i][t1]或h[i][t2]都要將t1++或t2++,因爲每個大小的值不能重複