这个题目我昨晚看到的,没什么思路,因为马里奥有boot加速器,只要中间没有城堡,即可不耗时间和脚力,瞬间移动不超过L距离,遇见城堡就要停下来,当然不能该使用超过K次。。。我纠结了很久,最终觉得还是只能写个BFS,剪了下枝,不出意料还是TLE。。。
后来还是找的别人博客看了一下。。。其实之前也做了好多DP,也应该能想到,既然加速器可以用k次,则,每个点都有k个状态,通过DP,把各个状态进行下取优,就可以了。。。
这不得不让我对DP有了些新的理解,DP在状态转移的时候,就好像最短路里面的松弛操作,或者二者只是外表的不同,本质是遵循一个道理。
当然在DP之前还需要一些处理
首先用个g[][]二维数组把题目所给的路径给存下来,再用Floyd把点到点的最短路先求出来,当然不是完全的Floyd,因为这个floyd求出来的最短路完全是为了之后用加速器,加速器不允许途中有城堡,因此在floyd的时候要加判断条件,有城堡在中间就不走。
用一个d[i][k]表示i点在加速器使用了k次的最短路径。
一开始还以为是总加速距离不能超过L,后来发现原来是每次。要细心
#include <iostream> #include <cstdio> #include <cstring> #define N 110 #define INF 1<<29 using namespace std; int A,B,M,L,K; int g[N][N],d[N][15]; int q[N*20],st[N*20],inq[N][20]; void init() {for (int i=0;i<=A+B;i++){for (int j=0;j<=A+B;j++){if (i==j){g[i][j]=0;}elseg[i][j]=INF;}} } void floyd() {int i,j,k;for (k=1;k<=A+B;k++){for (i=1;i<=A+B;i++){for (j=1;j<=A+B;j++){if (k>A) continue;//Floyd 只求能用加速器飞越的最短路if (g[i][j]>g[i][k]+g[k][j])g[i][j]=g[i][k]+g[k][j];//cout<<g[i][j]<<" "<<i<<" "<<j<<endl; }}} } void solve() {int maxn=(K+1)*(A+B);int front=0,rear=0;memset(inq,0,sizeof inq);for (int i=1;i<=A+B;i++){for (int j=0;j<=K;j++){d[i][j]=INF;//cout<<d[i][j]<<endl; }}d[A+B][0]=0; //初始状态q[rear]=A+B; //这次尝试了一下手动队列,而不是STL队列,效果相同,不过手工的队列时间应该好一些。st[rear]=0;rear++;while (front!=rear){int u=q[front];int k=st[front];front++;if (front>maxn)front=0;inq[u][k]=0;// cout<<u<<" "<<k<<" "<<d[u][k]<<endl;for (int i=1;i<=A+B;i++){if (d[i][k]>d[u][k]+g[u][i]) //进行普通最短路,保存当前状态{d[i][k]=d[u][k]+g[u][i];if (!inq[i][k]){q[rear]=i;st[rear]=k;rear++;if (rear>maxn)rear=0;inq[i][k]=1;}}if (g[u][i]<=L && k<K && d[u][k]<d[i][k+1]) //如果能够进行加速,并且加速后能更新加速后的状态,则加速,并且保存该状态。{d[i][k+1]=d[u][k];if (!inq[i][k+1]){inq[i][k+1]=1;q[rear]=i;st[rear]=k+1;rear++;if (rear>maxn)rear=0;}}}} } int main() {int t;scanf("%d",&t);while (t--){scanf("%d%d%d%d%d",&A,&B,&M,&L,&K);init();for (int i=1;i<=M;i++){int a,b,c;scanf("%d%d%d",&a,&b,&c);g[a][b]=g[b][a]=c;}floyd();solve();int ans=INF;for (int i=0;i<=K;i++)if (ans>d[1][i])ans=d[1][i];printf("%d\n",ans);}return 0; }
其实整个动规过程就是最短路的松弛过程,尤其是它把每个点的状态都求到了,并且把松弛成功(或者说状态转移成功)的点又存贮进了队列,以它来继续寻求更新其他点,这种思想应该可以再用到以后其他的DP问题中