题目大意:有A个村庄,B个城堡,村庄的序号为1---A,城堡的序号为A----A+B,马里奥有一双魔法鞋子,可以瞬移K次,魔法鞋子可以让他在两点之间的距离<=L的时候瞬移,期间不花费一点时间,但前提是这两点之内不包含城堡,问马里奥要从点A+B到点1的最短的时间
解题思路:先用floyd求出两点间的最短距离,再用spfa算出最短距离,这里的d数组要变成二维的,d[i][j],i表示点,j表示使用了魔法鞋子的次数,d[i][j]就表示从点i到点A+B使用了j次魔法鞋子的最短距离,这时候判断就要有两个,一个是使用了同样次数的魔法鞋子的判断,一个是多使用了一次的判断,详情请看代码
#include<cstdio>
#include<cstring>
#include<queue>
#define maxn 110
#define maxm 1100
#define INF 0x3f3f3f3f
using namespace std;
int A,B,M,L,K,N;
int f[maxn][maxn],d[maxn][maxn],vis[maxn][maxn];
int t[maxm],q[maxm];
void init() {
scanf("%d%d%d%d%d",&A,&B,&M,&L,&K);
N = A + B;
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
if(i == j)
f[i][j] = 0;
else
f[i][j] = INF;
int u,v,t;
for(int i = 0; i < M; i++) {
scanf("%d%d%d",&u,&v,&t);
f[u][v] = f[v][u] = t;
}
}
void floyd() {
//k <= A表示经过的是村庄
for(int k = 1; k <= N; k++)
for(int i = 1; i <= N; i++)
for(int j = 1; j <= N; j++)
if( k <= A && f[i][k] + f[k][j] < f[i][j])
f[i][j] = f[i][k] + f[k][j];
}
int spfa() {
//k是从0开始,最多有k次,所以有k+1层
int max = (K + 1) * N;
memset(vis,0,sizeof(vis));
for(int i = 1; i <= N; i++)
for(int j = 0; j <= K; j++)
d[i][j] = INF;
d[N][0] = 0;
int front , rear;
front = rear = 0;
q[rear] = N;
t[rear] = 0;
rear++;
int u,k;
while(rear != front) {
u = q[front];
k = t[front];
front++;
if(front > max)
front = 0;
vis[u][k] = 0;
for(int v = 1; v <= N; v++) {
//在同一层
if( d[u][k] + f[u][v] < d[v][k]) {
d[v][k] = d[u][k] + f[u][v];
if(!vis[v][k]) {
q[rear] = v;
t[rear] = k;
rear++;
if(rear > max)
rear = 0;
vis[v][k] = 1;
}
}
//不同层,使用了魔法鞋子
if(f[u][v] <= L && k < K && d[u][k] < d[v][k+1]) {
d[v][k+1] = d[u][k];
if(!vis[v][k+1]) {
q[rear] = v;
t[rear] = k + 1;
rear++;
if(rear > max)
rear = 0;
vis[v][k+1] = 0;
}
}
}
}
int ans = INF;
for(int i = 0; i <= K; i++)
if(d[1][i] < ans)
ans = d[1][i];
return ans;
}
int main() {
int test;
scanf("%d",&test);
while(test--) {
init();
floyd();
printf("%d\n",spfa());
}
return 0;
}