CJOJ 2422 [USACO Mar08] 奶牛跑步

奶牛跑步

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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#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++,因为每个大小的值不能重复


你可能感兴趣的:(考试)