【动态规划】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啊,还是人太菜了。

主要的一些点就是:

  1. 飞到上边界不会死
  2. 同一秒可以多次跳跃
  3. 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
%d
",ans);
79     }else printf("0
%d
",mx);
80     return 0;
81 }

END

原文地址:https://www.cnblogs.com/antiquality/p/9286121.html