CF187B AlgoRace
题意翻译
有nn 个城市,两两之间有直接连边,还有mm 辆车。
已知这mm 辆车在(i,j)(i,j) 边上需要w_{i,j}w**i,j 的时间,但是你可以在到达一个城市之后选择换车,换车视为瞬间完成。对于每组询问(s,t,k)(s,t,k) ,求s o ts→t 的最短时间,其中换车总次数不超过k-1k−1 ,即全程使用的车次不超过kk 。
询问有rr 组数据。其中
n,mle 60 , w_{i,j}le 10^6 , rle 10^5n,m≤60,w**i,j≤106,r≤105 ;
1le s,tle n,kle 10001≤s,t≤n,k≤1000 .
感谢@frankchenfu 提供的翻
题解:
一眼DP,看到这个数据范围觉得应该是多维状态。
所以直接按题意设:
(dp[i][j][k])表示从i到j一共使用的车次不超过k的最短时间
那么可以看出,这个转移和中转点有关系。也就是枚举断点,类似(Floyd)的转移。但是还有这个换车的一维比较难搞。
稍加思索,觉得转移应该是这样的:
[dp[i][j][k]=min(dp[i][l][k-1]+dp[l][j][0])quad(lin (1,n))
]
也就是,先换不超过k-1次车,最后不换车直接到达,因为断点处必然换一次车。
那么初值也就是不换车直达的时间。这个需要开始就做一遍松弛。
然后就看代码吧:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxq=1e5+5;
int n,m,r,maxx;
int dp[62][62][62];
//dp[i][j][k]表示i->j换k次车的最小时间
int a[62][62][62];
//a[k][i][j]表示i->j的原始时间
int q[maxq][3];
int main()
{
scanf("%d%d%d",&n,&m,&r);
for(int k=1;k<=m;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[k][i][j]);
for(int i=1;i<=r;i++)
{
scanf("%d%d%d",&q[i][0],&q[i][1],&q[i][2]);
maxx=max(maxx,q[i][2]);
}
maxx=min(maxx,n);
for(int op=1;op<=m;op++)
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
a[op][i][j]=min(a[op][i][j],a[op][i][k]+a[op][k][j]);
memset(dp,127,sizeof(dp));
for(int op=1;op<=m;op++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][0]=min(dp[i][j][0],a[op][i][j]);
for(int k=1;k<=maxx+1;k++)
{
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][k]=dp[i][j][k-1];
for(int l=1;l<=n;l++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j][k]=min(dp[i][j][k],dp[i][l][k-1]+dp[l][j][0]);
}
for(int i=1;i<=r;i++)
{
q[i][2]=min(q[i][2],maxx);
printf("%d
",dp[q[i][0]][q[i][1]][q[i][2]]);
}
return 0;
}