【动态规划】luoguP1941 飞扬的小鸟
细节总是打挂选手:)
题目描述
Flappy Bird
是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:
游戏界面是一个长为 nn ,高为 mm 的二维平面,其中有 kk 个管道(忽略管道的宽度)。
小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
小鸟每个单位时间沿横坐标方向右移的距离为 11 ,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 XX ,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 YY 。小鸟位于横坐标方向不同位置时,上升的高度 XX 和下降的高度 YY 可能互不相同。
小鸟高度等于 00 或者小鸟碰到管道时,游戏失败。小鸟高度为 mm 时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。
输入输出格式
输入格式:
第 11 行有 33 个整数 n, m, kn,m,k ,分别表示游戏界面的长度,高度和水管的数量,每两个整数之间用一个空格隔开;
接下来的 nn 行,每行 22 个用一个空格隔开的整数 XX 和 YY ,依次表示在横坐标位置 0 \sim n-10∼n−1 上玩家点击屏幕后,小鸟在下一位置上升的高度 XX ,以及在这个位置上玩家不点击屏幕时,小鸟在下一位置下降的高度 YY 。
接下来 kk 行,每行 33 个整数 P, L, HP,L,H ,每两个整数之间用一个空格隔开。每行表示一个管道,其中 PP 表示管道的横坐标, LL 表示此管道缝隙的下边沿高度, HH 表示管道缝隙上边沿的高度(输入数据保证 PP 各不相同,但不保证按照大小顺序给出)。
输出格式:
共两行。
第一行,包含一个整数,如果可以成功完成游戏,则输出 11 ,否则输出 00 。
第二行,包含一个整数,如果第一行为 11 ,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。
题目分析
dp方程当然是很简单的,就是个普普通通的计数dp。
但是细节……呃,反正第一次提交(赶着吃午饭)的话只有60pts……改了一下zz错误是75pts……
哎……可能在NOIP考场上最多只能得75pts啊,还是人太菜了。
主要的一些点就是:
- 飞到上边界不会死
- 同一秒可以多次跳跃
- dp时间复杂度的小优化
前两个都是明眼人都能看出来的,第三个的话还是要一定dp的功底吧。
具体来说就是因为同一秒能够跳很多次,那么如果每一次跳都拿来转移的话,显然(然而之前我并没有仔细分析……)这个复杂度可以被卡上天。
那么实际上跳多次可以看成是这个东西。也就是从同一横坐标的地方直接跳上来。
具体实现好像细节很多?但也不算很多……可能还是本身太菜了,总是打挂。
这里有一篇很好的讲解:https://www.luogu.org/blog/xxzh2425/fei-yang-di-xiao-niao-ti-xie-p1941-post
1 #include<bits/stdc++.h> 2 const int maxn = 10035; 3 const int maxm = 1003; 4 5 int x[maxn],y[maxn],l[maxn],r[maxn]; 6 int f[maxn][maxm]; 7 bool vis[maxn]; 8 int n,m,k,mx; 9 10 int read() 11 { 12 char ch = getchar(); 13 int num = 0; 14 bool fl = 0; 15 for (; !isdigit(ch); ch = getchar()) 16 if (ch=='-') fl = 1; 17 for (; isdigit(ch); ch = getchar()) 18 num = (num<<1)+(num<<3)+ch-48; 19 if (fl) num = -num; 20 return num; 21 } 22 bool dp() 23 { 24 int tot = 0; 25 for (int i=1; i<=n; i++) 26 { 27 bool fl = 0; 28 for (int j=x[i-1]; j<=m; j++) 29 { 30 if (j==m) 31 for (int h=m-x[i-1]; h<=m; h++) 32 f[i][m] = std::min(f[i][m], f[i-1][h]+1), 33 f[i][m] = std::min(f[i][m], f[i][h]+1); 34 // if (j-x[i-1]<=r[i-1]&&j-x[i-1]>=l[i-1]) 35 f[i][j] = std::min(f[i][j], f[i-1][j-x[i-1]]+1); 36 // if (j-x[i-1]>=l[i]&&j-x[i-1]<=r[i]) 37 f[i][j] = std::min(f[i][j], f[i][j-x[i-1]]+1); 38 } 39 for (int j=l[i]; j<=r[i]; j++) 40 { 41 if (j+y[i-1]>=l[i-1]&&j+y[i-1]<=r[i-1]) 42 f[i][j] = std::min(f[i][j], f[i-1][j+y[i-1]]); 43 if (f[i][j]!=f[0][0]) fl = 1; 44 } 45 for (int j=l[i]-1; j>=1; j--) 46 f[i][j] = f[0][0]; 47 for (int j=r[i]+1; j<=m; j++) 48 f[i][j] = f[0][0]; 49 if (!fl){ 50 mx = tot; 51 return 0; 52 } 53 if (vis[i]) tot++; 54 } 55 return 1; 56 } 57 int main() 58 { 59 // freopen("testdata.in","r",stdin); 60 memset(f, 0x3f3f3f3f, sizeof f); 61 n = read(), m = read(), k = read(); 62 for (int i=0; i<n; i++) 63 { 64 x[i] = read(), y[i] = read(); 65 l[i] = 1, r[i] = m; 66 } 67 l[n] = 1, r[n] = m; 68 for (int i=1; i<=m; i++) f[0][i] = 0; 69 for (int i=1; i<=k; i++) 70 { 71 int a = read(), b = read(), c = read(); 72 vis[a] = 1, l[a] = b+1, r[a] = c-1; 73 } 74 if (dp()){ 75 int ans = f[0][0]; 76 for (int i=1; i<=m; i++) 77 ans = std::min(ans, f[n][i]); 78 printf("1\n%d\n",ans); 79 }else printf("0\n%d\n",mx); 80 return 0; 81 }
END