CodeForces1249E - By Elevator or Stairs?-好理解自己想不出来的dp
题意:
给出n、c,表示有n层楼,等电梯需要c时间;
给出两行数,每行有n-1个数,
第一行stairs代表从1楼到每层楼走楼梯需要的时间
第二行elevator代表从1楼到每层楼乘电梯需要的时间;
需要注意的是,从电梯转电梯不需要等待时间,从楼梯转楼梯也不需要等待时间,
但是从楼梯转电梯需要算上等待的时间 t 。
求:
从1楼到每层楼所需的最短时间,输出应该有n-1个时间。
思路:
由于存在两种状态,所以可以用0代表乘电梯,1代表走楼梯;
所以一开始可以按照题目所给的开一个三维数组dp [ i ] [ j ] [ k ],i代表乘电梯还是走楼梯,j代表当前到达第几层,k代表将要去第几层。
所以一开始进行dp清空
接下来赋初值,由于不知道从1楼往上走是等电梯还是走楼梯,所以假设等电梯的话需要赋初值dp=c,算上等电梯的时间。
初始化如下:
memset(dp,0,sizeof(dp));
dp[1][0][0]=c;//电梯来电梯走,需要等电梯 dp[1][0][1]=c;//电梯来楼梯走,需要等电梯 dp[1][1][0]=0;//楼梯来电梯走 dp[1][1][1]=0;//楼梯来楼梯走
之后不管是走楼梯还是乘电梯都需要加上楼梯到楼梯或者电梯到电梯转的时间。如果碰到楼梯转电梯的情况,加上c即可。
动态转移方程如下:
dp[i][0][1]=min(dp[i-1][0][0]+b[i-1],dp[i-1][1][0]+b[i-1]+c); dp[i][0][0]=min(dp[i-1][0][0]+b[i-1],dp[i-1][1][0]+b[i-1]+c); dp[i][1][0]=min(dp[i-1][0][1]+a[i-1],dp[i-1][1][1]+a[i-1]); dp[i][1][1]=min(dp[i-1][0][1]+a[i-1],dp[i-1][1][1]+a[i-1]);
三维代码:
1 #include<stdio.h> 2 #include<algorithm> 3 #include<string.h> 4 using namespace std; 5 const int N=2e5+20; 6 7 int a[N],b[N]; 8 int dp[N][2][2]; 9 10 //dp[i][j][k] 到达第几层,j来,k走 11 //0电梯 1楼梯 12 int main() 13 { 14 int n,c; 15 while(~scanf("%d %d",&n,&c)) 16 { 17 memset(dp,0,sizeof(dp)); 18 // for(int i=2;i<=n;i++)//0 7 13 18 24 35 36 37 40 45 19 // wa 0 0 7 13 18 24 35 36 37 40 20 for(int i=1; i<n; i++) 21 scanf("%d",&a[i]);//楼梯1 22 for(int i=1; i<n; i++) 23 scanf("%d",&b[i]);//电梯0 24 dp[1][0][0]=c;//电梯来电梯走,需要等电梯 25 dp[1][0][1]=c;//电梯来楼梯走,需要等电梯 26 dp[1][1][0]=0;//楼梯来电梯走 27 dp[1][1][1]=0;//楼梯来楼梯走 28 for(int i=2; i<=n; i++) 29 { 30 dp[i][0][1]=min(dp[i-1][0][0]+b[i-1],dp[i-1][1][0]+b[i-1]+c); 31 dp[i][0][0]=min(dp[i-1][0][0]+b[i-1],dp[i-1][1][0]+b[i-1]+c); 32 dp[i][1][0]=min(dp[i-1][0][1]+a[i-1],dp[i-1][1][1]+a[i-1]); 33 dp[i][1][1]=min(dp[i-1][0][1]+a[i-1],dp[i-1][1][1]+a[i-1]); 34 } 35 printf("0 "); 36 for(int i=2;i<=n;i++) 37 { 38 if(i!=2) 39 printf(" "); 40 int w=min(dp[i][0][1],dp[i][0][0]); 41 int ww=min(w,dp[i][1][0]); 42 int www=min(ww,dp[i][1][1]); 43 printf("%d",www); 44 } 45 printf("\n"); 46 } 47 return 0; 48 }
优化后的二维代码:
经过三维数组观察可得:
dp[i][0][1]=dp[i][0][0]=min(dp[i-1][0][0]+b[i-1],dp[i-1][1][0]+b[i-1]+c); dp[i][1][0]=dp[i][1][1]=min(dp[i-1][0][1]+a[i-1],dp[i-1][1][1]+a[i-1]);
所以与从哪来有关,与到哪去无关
从而可以写成二维数组
代码如下:
#include<stdio.h> #include<algorithm> #include<string.h> using namespace std; const int N=2e5+20; int a[N],b[N],dp[N][2]; //dp[i][j][k] 到达第几层,j来,k走 //0电梯 1楼梯 int main() { int n,c; while(~scanf("%d %d",&n,&c)) { memset(dp,0,sizeof(dp)); for(int i=1; i<n; i++) scanf("%d",&a[i]);//楼梯1 for(int i=1; i<n; i++) scanf("%d",&b[i]);//电梯0 dp[1][0]=c; dp[1][1]=0; for(int i=2; i<=n; i++) { dp[i][0]=min(dp[i-1][0]+b[i-1],dp[i-1][1]+b[i-1]+c); dp[i][1]=min(dp[i-1][0]+a[i-1],dp[i-1][1][1]+a[i-1]); } printf("0 "); for(int i=2;i<=n;i++) { if(i!=2) printf(" "); int w=min(dp[i][0],dp[i][1]); printf("%d",w); } printf("\n"); } return 0; }