题目链接

floyd+dijkstra+DP

对于传统的Dijkstra, 给每一个节点设置一个数组 d[i] 用来记录从起点到当前节点i的最短距离,通过已经确定了的节点距离d[],可以得出从起点到当前节点的最短距离。 如果把传统Dijkstra的数组d[i],改成dp[i],那么就可以延伸出另一种说法:对于状态数有限的问题,只要能确定起点状态,那么就能通过起点状态,不断更新其他状态,最终状态会逐个确定下来,求出来的值是从起点状态到当前状态的极值。

对于这题而言,每一个节点可以用状态 dp[i][j] 来表示,即从起点到i这个节点时,还剩j次使用靴子的次数,根据上面的结论,这道题就很好解决了。
由于使用靴子是有限制条件的:1. 使用靴子行走的最长路径不能超过L; 2. 使用靴子行走的过程中不能穿过城堡。所以要预先处理出可以使用靴子的情况。

floyd在枚举中间点松弛两点之间的距离时,可以给这个中间节点加个限制条件,即只枚举表示村子的节点去做松弛,最终得到的就是不经过城堡的情况下,两点间的最短距离。
对于当前状态d[u][j],可以更新两种状态:1. 不使用靴子的情况 dp[v][j] = dp[u][j]+cost[u][v]; 2. 使用靴子的情况下 dp[v][j-1] = dp[u][j]。 (u和v之间能使用靴子并且两点之间的距离 <= L)。

#include <bits/stdc++.h>

using namespace std;

const int Maxn = 100+10;
const int INF = 0x3f3f3f3f;

struct Node {
    int v, w, cnt;
    Node(int x, int y, int z):v(x), w(y), cnt(z) {}
    bool operator < (const Node a1) const {
        return w > a1.w;
    }
};

int G[Maxn][Maxn], f[Maxn][Maxn];
bool vis[Maxn][Maxn];

int main(void)
{
    ios::sync_with_stdio(false);
    int T;
    cin >> T;
    while(T--) {
        int a, b, m, L, k;
        cin >> a >> b >> m >> L >> k;
        int u, v, w;
        memset(G, INF, sizeof(G));
        memset(f, INF, sizeof(f));
        for(int i = 0; i < m; ++i) {
            cin >> u >> v >> w;
            G[u][v] = G[v][u] = w;
            f[u][v] = f[v][u] = w;
        }
        for(int kk = 1; kk <= a; ++kk) {
            for(int i = 1; i <= a+b; ++i) {
                for(int j = 1; j <= a+b; ++j) {
                    if(f[i][kk] != INF && f[kk][j] != INF)
                        f[i][j] = min(f[i][j], f[i][kk]+f[kk][j]);
                }
            }
        }

        int ans = INF;
        priority_queue <Node> qu;
        memset(vis, false, sizeof(vis));
        qu.push(Node(a+b, 0, k));
        while(!qu.empty()) {
            Node tmp = qu.top(); qu.pop();
            if(tmp.v == 1) {
                ans = min(tmp.w, ans);
            }
            if(vis[tmp.v][tmp.cnt]) continue;
            vis[tmp.v][tmp.cnt] = true;
            for(int v = 1; v <= a+b; ++v) {
                if(!vis[v][tmp.cnt] && G[tmp.v][v] != INF)
                    qu.push(Node(v, tmp.w+G[tmp.v][v], tmp.cnt));
                if(!vis[v][tmp.cnt-1] && tmp.cnt > 0 && f[tmp.v][v] <= L) {
                    qu.push(Node(v, tmp.w, tmp.cnt-1));
                }
            }
        }
        cout << ans << endl;
    }
    return 0;
}