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;
}
浙公网安备 33010602011771号