题目意思很简单:
有A个村子和B个城堡,村子标号是1~A,城堡标号是A+1~B。马里奥现在位于城堡B,他要带公主回到村子1,他有一双靴子,穿上之后可以不用时间就能从一个地方飞到另外一个地方,但是穿着靴子不能穿过城堡,穿靴子的次数也不能超过 K 次,一次不能超过 L km。求从 B 到 1 所用的最短时间。
分析:
最开始是在一本书上看到这道题目,说是Dijstra,于是打算来写写看。结果悲剧了。题目的关键点其实是在哪里使用鞋子才能使得全局的时间最少,可以用 DP 来解决,用一种逆向思维,先来写动态转移方程,用 i 来表示目前到了 i 点,用 j 来表示到 i 点时穿了几次靴子,dp[i][j] = min{dp[k][j-1],dp[k][j]+map[k][i]} 从方程中可以看出,需要知道任意两个点之间的距离,也需要知道任意两个点之间是否能够穿靴子通过(比如考虑到 K,L),此时就需要用到求最短路径了,由于此时用到的是任意两个点之间的最短路径,所以最好用 Floyd ,用 Dijstra 的话在求任意两个点之间是否能够穿靴子通过时就会出现点小麻烦。
好吧,其实我两种都写了。
有A个村子和B个城堡,村子标号是1~A,城堡标号是A+1~B。马里奥现在位于城堡B,他要带公主回到村子1,他有一双靴子,穿上之后可以不用时间就能从一个地方飞到另外一个地方,但是穿着靴子不能穿过城堡,穿靴子的次数也不能超过 K 次,一次不能超过 L km。求从 B 到 1 所用的最短时间。
分析:
最开始是在一本书上看到这道题目,说是Dijstra,于是打算来写写看。结果悲剧了。题目的关键点其实是在哪里使用鞋子才能使得全局的时间最少,可以用 DP 来解决,用一种逆向思维,先来写动态转移方程,用 i 来表示目前到了 i 点,用 j 来表示到 i 点时穿了几次靴子,dp[i][j] = min{dp[k][j-1],dp[k][j]+map[k][i]} 从方程中可以看出,需要知道任意两个点之间的距离,也需要知道任意两个点之间是否能够穿靴子通过(比如考虑到 K,L),此时就需要用到求最短路径了,由于此时用到的是任意两个点之间的最短路径,所以最好用 Floyd ,用 Dijstra 的话在求任意两个点之间是否能够穿靴子通过时就会出现点小麻烦。
好吧,其实我两种都写了。
贴下Dijstra的代码
#include<stdio.h>
const int maxn = 150;
const int INF = 1000000000;
int mat[maxn][maxn],tmp[maxn][maxn],Dist[maxn];
int DP[maxn][20],newmat[maxn][maxn];
bool visited[maxn];
int n,A,B,L;
void Dijstra(int s)
{
int i,j,k,min;
for(i=1;i<=n;i++)
{
Dist[i] = mat[s][i];
visited[i] = false;
}
Dist[s] = 0;
visited[s] = true;
for(i=1;i<=n;i++)
{
k = -1;
min = INF;
for(j=1;j<=n;j++)
if(visited[j]==false&&min>Dist[j])
{
k = j;
min = Dist[j];
}
if(k!=-1)
{
visited[k] = true;
for(j=1;j<=n;j++)
{
if(Dist[k]+mat[k][j]<Dist[j])
{
Dist[j] = Dist[k] + mat[k][j];
}
}
}
}
}
void Dijstra1(int s)//为了求tmp[][],tmp[][]保存的是任意两点是否能穿鞋子通过。
{
int i,j,k,min;
for(i=1;i<=n;i++)
{
Dist[i] = mat[s][i];
visited[i] = false;
}
Dist[s] = 0;
visited[s] = true;
for(i=1;i<=n;i++)
{
k = -1;
min = INF;
for(j=1;j<=n;j++)
if(visited[j]==false&&min>Dist[j])
{
k = j;
min = Dist[j];
}
if(k!=-1)
{
visited[k] = true;
for(j=1;j<=n;j++)
{
if(Dist[k]+mat[k][j]<Dist[j]&&k<=A)
{
Dist[j] = Dist[k]+mat[k][j];
if(Dist[j]<=L)
tmp[s][j] = 0;
}
}
}
}
}
int main()
{
int i,j,t,m,K;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&A,&B,&m,&L,&K);
n = A+B;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
mat[i][j] = newmat[i][j] = tmp[j][i] = INF;//初始化
int a,b,c;
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
mat[a][b] = mat[b][a] = c;
if(c<=L)
tmp[a][b] = tmp[b][a] = 0;
newmat[a][b] = newmat[b][a] = c;
}//构图
for(i=1;i<=n;i++)
{
Dijstra1(i);
Dijstra(i);
for(j=1;j<=n;j++)
newmat[i][j] = Dist[j];
}
for(i=1;i<=n;i++)
DP[i][0] = newmat[1][i];//未使用加速靴时的时间
for(j=1;j<=K;j++)
DP[1][j] = 0;
for(i=2;i<=n;i++)
{
for(j=1;j<=K;j++)
{
int min = INF,tt;
for(int l = 1;l<i;l++)
{
if(!tmp[l][i])
tt = DP[l][j-1]>DP[l][j]+newmat[l][i]?DP[l][j]+newmat[l][i]:DP[l][j-1];
else
tt = newmat[l][i]+DP[l][j];
if(tt<min)
min = tt;
}
DP[i][j] = min;
}
}
printf("%d\n",DP[n][K]);
}
return 0;
}
再来比对一下Floyd的代码
#include<stdio.h>
const int maxn = 150;
const int INF = 1000000000;
int dp[maxn][20],mat[maxn][maxn],mind[maxn][maxn],tmp[maxn][maxn];
int n,A,B,L;
void Floyd()
{
int i,j,k;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
mind[i][j] = mat[i][j];
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(mind[i][k]+mind[k][j]<mind[i][j])
{
mind[i][j] = mind[i][k] + mind[k][j];
if(k<=A&&mind[i][j]<=L)
tmp[i][j] = 1;
}
}
}
}
}
int main()
{
int t,K,m,i,j,a,b,c;
scanf("%d",&t);
while(t--)
{
scanf("%d%d%d%d%d",&A,&B,&m,&L,&K);
n = A+B;
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
if(i==j)
mat[i][j] = 0;
else
mat[i][j] = INF;
tmp[i][j] = 0;
}
}//初始化
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&a,&b,&c);
mat[a][b] = mat[b][a] = c;
if(c<=L)
tmp[a][b] = tmp[b][a] = 1;
}//初步构图
Floyd();//mind[][]保存计算之后的最短路径
for(i=1;i<=n;i++)
dp[i][0] = mind[1][i];
for(i=1;i<=K;i++)
dp[1][i] = 0;
for(i=2;i<=n;i++)
{
for(j=1;j<=K;j++)
{
int min = INF,tt;
for(int l=1;l<i;l++)
{
if(tmp[l][i])
tt = dp[l][j-1]<dp[l][j]+mind[l][i]?dp[l][j-1]:dp[l][j]+mind[l][i];
else
tt = dp[l][j] + mind[l][i];
if(tt<min)
min = tt;
}
dp[i][j] = min;
}
}
printf("%d\n",dp[n][K]);
}
return 0;
}