简介:
再次成功营救出桃子公主的马里奥需要从大魔王Bowser的城堡返回ta的家
马里奥的世界里有A座村庄,B座城堡,其中村庄的标号为1~A,城堡的编号为A+1~A+B
马里奥住在村庄1,而Bowser住在城堡A+B
马里奥能以一小时一千米的速度沿着村庄和城堡之间的双向道路行走,也可以使用魔法鞋加速,
魔法鞋可以让马里奥瞬间移动不超过L千米,但由于城堡中有很多陷阱,
聪明的马里奥是不会冒风险穿魔法鞋穿过城堡的
此外,使用魔法鞋的起点和终点都只能是城堡和村庄
最多只能使用K次魔法鞋,计算马里奥所需的最短时间
(史上最长简介。。。)
分析:
如果没有魔法鞋,我们直接来一个最短路即可
有了这层限制之后,我第一个反应就是dp
设计状态:f[i][j]表示到达i,使用魔法鞋的次数是k时的最短路
用一个类似spfa的结构完成dp
实际上我的思路已经很接近正解了
正解在dp之前进行了一步预处理:
用floyed求任意两点之间不经过城堡的最短路
这样是因为魔法鞋只是不能在城堡中用,
但是我们可以穿着魔法鞋穿过若干村庄
经过预处理后,我们记录的所有边就可以一视同仁的使用魔法鞋了
之后的dp
我们可以用spfa,也可用dijkstra
相对而言,diskstra比较优美一些
(有一点像次短路的转移)
tip
使用魔法鞋时,终点和起点一定是城堡或村庄
if (f[p][q]+G[p][j]>f[j][q]) //这已经不是纯正的dij了
{ //这是dij和spfa的结合
f[j][q]=f[p][q]+G[p][j];
vis[j][q]=1;
}
if (f[j][q+1]>f[p][q]&&G[p][j]<=L)
{
f[j][q+1]=f[p][q];
vis[j][q+1]=1;
}
网上的代码是这样写的(如上),但是整张图并不会出现负边,所以朴素的dijkstra应该是没有问题的
//这里写代码片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int INF=0x33333333;
int f[102][12];
int a,b,m,K,L;
int G[102][102];
bool vis[102][12];
void doit()
{
memset(f,0x33,sizeof(f));
memset(vis,1,sizeof(vis));
int n=a+b;
f[n][0]=0;
int tt=(K+1)*n; //需要松弛的次数等于状态数
for (int i=1;i<=tt;i++)
{
int p=-1,q=-1,minn=INF;
for (int j=1;j<=n;j++)
for (int k=0;k<=K;k++)
if (f[j][k]<minn&&vis[j][k])
{
p=j; q=k; minn=f[j][k];
}
vis[p][q]=0;
for (int j=1;j<=n;j++)
{
f[j][q]=min(f[j][q],f[p][q]+G[p][j]);
if (G[p][j]<=L)
f[j][q+1]=min(f[j][q+1],f[p][q]);
}
}
}
void floyed() //任意两点之间不经过城堡的最短路
{
int i,j,k;
for (int i=1;i<=a+b;i++) G[i][i]=0;
for (i=1;i<=a;i++)
for (j=1;j<=a+b;j++)
if (i!=j)
for (k=1;k<=a+b;k++)
if (k!=j)
G[j][k]=min(G[j][k],G[j][i]+G[i][k]);
}
int main()
{
int T;
scanf("%d",&T);
while (T--)
{
memset(G,0x33,sizeof(G));
scanf("%d%d%d%d%d",&a,&b,&m,&L,&K);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
G[x][y]=G[y][x]=z;
}
floyed();
doit();
int ans=INF;
for (int i=0;i<=K;i++)
ans=min(ans,f[1][i]);
printf("%d\n",ans);
}
return 0;
}