题意

给你n个点、m条有向边的图,边有长度,现在要从1号点走向n号点,希望在总路径不超过T的情况下经过尽可能多的点。题目保证有解,输出经过的点的方案。

想法

二维spfa,后来理解了一下其实就是DAG上的dp。f[i][j]表示到了点i,已经经过了j个点的最小距离。对于f[i][j]这个状态,肯定是经过的路径越小越好,因为题目要求总路径不能超过T,因此是无后效性的。转移的过程也就是spfa的过程,这也就是“二维spfa”这个名字的由来吧。

Code

#include <cstdio>
#include <cstdlib>
#include <queue>
#include <cstring>
#include <iostream>
#define ll long long 
#define db double
#define N 5011
#define mk make_pair
#define max(a, b) ((a) > (b) ? (a) : (b))
#define min(a, b) ((a) < (b) ? (a) : (b))
#define swap(T, a, b) ({T ttt = a; a = b; b = ttt;})
using namespace std;

int head[N], l = 0; 
struct Edge { int from, to, len, next; } f[N]; 
int n, m, cnt = 0, ans[N], pre[N][N]; 
int T, d[N][N]; 
bool vis[N][N]; 
queue < pair<int, int> > q; 

void spfa()
{
    memset(d, 127, sizeof(d)); 
    memset(vis, false, sizeof(vis)); 
    d[1][0] = 0; vis[1][0] = true; pre[1][0] = 0; 
    q.push(mk(1, 0)); 
    while (!q.empty()) 
    {
        int x = q.front().first, y = q.front().second; q.pop(); 
        vis[x][y] = false; 
        for (int i = head[x]; i; i = f[i].next)
            if (d[x][y] + f[i].len < d[f[i].to][y+1] && d[x][y] + f[i].len <= T)
            {
                d[f[i].to][y+1] = d[x][y] + f[i].len; 
                pre[f[i].to][y+1] = x; 
                if (!vis[f[i].to][y+1])  
                {
                    vis[f[i].to][y+1] = true;
                    q.push(mk(f[i].to, y+1)); 
                }
            }
    }
}
int main()
{
    scanf("%d%d%d", &n, &m, &T); 
    int u, v, z; 
    for (int i = 1; i <= m; i++)
    {
        scanf("%d%d%d", &u, &v, &z); 
        f[++l] = (Edge) {u, v, z, head[u]}; head[u] = l; 
    }
    spfa(); int k = 1; 
    for (int i = n; i >= 1; i--)
        if (d[n][i] <= T) { k = i; break; } 
    printf("%d\n1 ", k+1); 
    for (int x = n, y = k; x > 0 && y > 0; x = pre[x][y--])
        ans[++cnt] = x; 
    for (int i = cnt; i >= 1; i--)
        printf("%d ", ans[i]);  
    return 0; 
}

zkGaia
1 声望0 粉丝

引用和评论

0 条评论