题目链接
题目描述
Flappy Bird是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:
游戏界面是一个长为n,高为m的二维平面,其中有k个管道(忽略管道的宽度)。
小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上升的高度X和下降的高度Y可能互不相同。
小鸟高度等于0或者小鸟碰到管道时,游戏失败。小鸟高度为m时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。
题目分析
这道题要求我们求出到达最右端的最少可能点击数,我们不妨先只看两步之间的关系——也就是从上一个横坐标位置到本横坐标位置的每一个纵坐标位置所需要的最小的点击数。由此,对于每一个纵坐标位置,实际上其最小点击数可能由上一步的某些纵坐标位置转移而来,并且所有的转移出发位置与终止位置的关系均属于一个方法集合。于是,我们发现,这道题的实质是背包问题。
把每一横坐标所对应纵坐标的集合作为背包,将每一种转移方法当作一个物品,其价值即为点击的总次数,而其费用即为上升或下降的纵坐标数,求在每一横坐标下花费每一费用所对应的最小价值,便可以将其当作背包问题进行求解。在转移之前判断是否存在障碍物,以此作为转移是否可行的依据。注意小鸟转移后原高度加上上升高度可以高于m,但此时需将其高度特判为m。
代码
1 int main() 2 { 3 scanf("%d%d%d",&n,&m,&k); 4 for(int i=1;i<=n;++i) 5 scanf("%d%d",&x[i],&y[i]); 6 memset(h,0x3f,sizeof h); 7 for(int i=1;i<=k;++i) 8 { 9 scanf("%d",&p[i]); 10 scanf("%d%d",&l[p[i]],&h[p[i]]); 11 } 12 memset(f,0x3f,sizeof f); 13 for(int i=1;i<=m;++i) 14 f[0][i]=0; 15 for(int i=1;i<=n;++i) 16 { 17 for(int j=x[i]+1;j<=x[i]+m;++j) 18 f[i][j]=min(f[i-1][j-x[i]]+1,f[i][j-x[i]]+1); 19 for(int j=m+1;j<=m+x[i];++j) 20 f[i][m]=min(f[i][m],f[i][j]); 21 for(int j=1;j<=m-y[i];++j) 22 f[i][j]=min(f[i][j],f[i-1][j+y[i]]); 23 for(int j=1;j<=l[i];++j) 24 f[i][j]=0x3f3f3f3f; 25 for(int j=h[i];j<=m;++j) 26 f[i][j]=0x3f3f3f3f; 27 } 28 for(int i=1;i<=m;++i) 29 ans=min(ans,f[n][i]); 30 if(ans<0x3f3f3f3f) 31 printf("1 %d",ans); 32 else 33 { 34 puts("0"); 35 int place,j; 36 ans=0; 37 for(place=n;place>0;--place) 38 { 39 for(j=1;j<=m;++j) 40 if(f[place][j]<0x3f3f3f3f) 41 break; 42 if(j<=m) 43 break; 44 } 45 for(int i=1;i<=place;++i) 46 if(h[i]!=0x3f3f3f3f) 47 ++ans; 48 printf("%d",ans); 49 } 50 return 0; 51 }