Flappy Bird 是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。
为了简化问题,我们对游戏规则进行了简化和改编:
- 游戏界面是一个长为 nn,高为 mm 的二维平面,其中有 kk 个管道(忽略管道的宽度)。
- 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
- 小鸟每个单位时间沿横坐标方向右移的距离为 11,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 XX,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 YY。小鸟位于横坐标方向不同位置时,上升的高度 XX 和下降的高度 YY 可能互不相同。
- 小鸟高度等于 00 或者小鸟碰到管道时,游戏失败。小鸟高度为 mm 时,无法再上升。
现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。
输入格式
第 11 行有 33 个整数 n,m,kn,m,k,分别表示游戏界面的长度,高度和水管的数量,每两个整数之间用一个空格隔开;
接下来的 nn 行,每行 22 个用一个空格隔开的整数 XX 和 YY,依次表示在横坐标位置 0~n?10~n?1 上玩家点击屏幕后,小鸟在下一位置上升的高度 XX,以及在这个位置上玩家不点击屏幕时,小鸟在下一位置下降的高度 YY。
接下来 kk 行,每行 33 个整数 P,L,HP,L,H,每两个整数之间用一个空格隔开。每行表示一个管道,其中 PP 表示管道的横坐标,LL 表示此管道缝隙的下边沿高度,HH 表示管道缝隙上边沿的高度(输入数据保证 PP 各不相同,但不保证按照大小顺序给出)。
输出格式
共两行。
第一行,包含一个整数,如果可以成功完成游戏,则输出 11,否则输出 00。
第二行,包含一个整数,如果第一行为 11,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。
样例一
input
10 10 6 3 9 9 9 1 2 1 3 1 2 1 1 2 1 2 1 1 6 2 2 1 2 7 5 1 5 6 3 5 7 5 8 8 7 9 9 1 3
output
1 6
样例二
input
10 10 4 1 2 3 1 2 2 1 8 1 8 3 2 2 1 2 1 2 2 1 2 1 0 2 6 7 9 9 1 4 3 8 10
output
0 3
限制与约定
对于 30%的数据:5≤n≤10,5≤m≤10,k=05≤n≤10,5≤m≤10,k=0,保证存在一组最优解使得同一单位时间最多点击屏幕 33 次;
对于 50%的数据:5≤n≤20,5≤m≤105≤n≤20,5≤m≤10,保证存在一组最优解使得同一单位时间最多点击屏幕 33 次;
对于 70%的数据:5≤n≤1000,5≤m≤1005≤n≤1000,5≤m≤100;
对于 100%的数据:5≤n≤10000,5≤m≤1000,0≤k<n,0<X<m,0<Y<m,0<P<n,0≤L<H≤m,L+1<H5≤n≤10000,5≤m≤1000,0≤k<n,0<X<m,0<Y<m,0<P<n,0≤L<H≤m,L+1<H。
时间限制:1s1s
空间限制:128MB
首先想到设分f[i][j]表示到达地i行第j列所需要的最少点击屏幕次数。转移方程为
f[ i ][ j ]=min{f[ i-1 ][ j - k*x[i-1] ] + k} (1<= k <= j/x) 上升—— ①
f[ i ][ j ]=min{f[ i-1 ][ j + y[i-1] } ( j + y[i-1] <= m) 下降
显然,下降可以O(1)转移,主要问题在上升的转移。
我们将上升的方程变一下:
f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ (j - x[i-1]) - (k-1)*x[i-1] ] + k -1} ——②
这是 f[ i ][ j - x[i-1] ] 的转移。
由 ② 化简可得:
f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ j - k*x[ i-1] ] + k -1}
消去f[ i-1 ][ j - k*x[ i-1] ]
f[ i ][ j ]= f[ i ][ j - x[ i-1 ] ]+1
于是就可以O(n*m)的时间内出解
1 #include <map> 2 #include <set> 3 #include <cmath> 4 #include <ctime> 5 #include <queue> 6 #include <stack> 7 #include <cstdio> 8 #include <string> 9 #include <vector> 10 #include <cstdlib> 11 #include <cstring> 12 #include <iostream> 13 #include <algorithm> 14 #define rg register 15 using namespace std; 16 #define ll long long 17 18 inline int gi() 19 { 20 rg bool b=0; 21 rg int r=0; 22 char c=getchar(); 23 while(c<‘0‘ || c>‘9‘) 24 { 25 if(c==‘-‘) b=!b; 26 c=getchar(); 27 } 28 while(c>=‘0‘ && c<=‘9‘) 29 { 30 r=r*10+c-‘0‘; 31 c=getchar(); 32 } 33 if(b) return -r; 34 return r; 35 } 36 37 const int inf = 2100000000, N = 10005, M = 1005; 38 int n,m,q,x[N],y[N],f[N][M]; 39 bool b[N]; 40 struct data 41 { 42 int up,down; 43 } da[N]; 44 45 int main() 46 { 47 freopen ("birda.in","r",stdin); 48 freopen ("birda.out","w",stdout); 49 int i,p,j,k,cnt,ans; 50 n=gi(), m=gi(), q=gi(); 51 for (i=0; i<n; i++) x[i]=gi(), y[i]=gi(); 52 for (i=1; i<=n; i++) da[i].down=0, da[i].up=m+1; 53 for (i=0; i<q; i++) p=gi(), da[p].down=gi(), da[p].up=gi(); //一定要加,不然会影响到第65行的循环枚举 54 for (i=1; i<=n; i++) for (j=0; j<=m; j++) f[i][j]=inf; //初始化。0位置除地面外都为0 55 f[0][0]=inf; 56 for (i=1; i<=n; i++) 57 { 58 for (j=x[i-1]; j<=m; j++) 59 { 60 f[i][j]=min(f[i][j],f[i-1][j-x[i-1]]+1), f[i][j]=min(f[i][j],f[i][j-x[i-1]]+1); //更新解,先不考虑水管 61 if (j == m) //特殊判断 j==m 的情况,因为不能超过 m ,所以有多种转移 62 for (k=m-x[i-1]; k<=m; k++) 63 f[i][j]=min(f[i][j],f[i-1][k]+1), f[i][j]=min(f[i][j],f[i][k]+1); 64 } 65 for (j=da[i].down+1; j<da[i].up; j++) //处理下落,必须是合法的 66 if (j+y[i-1] <= m) 67 f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]); 68 for (j=1; j<=da[i].down; j++) f[i][j]=inf; //考虑水管,去掉不合法的解 69 for (j=m; j>=da[i].up; j--) f[i][j]=inf; 70 } 71 cnt=q,ans=inf; 72 for (i=n; i>=1; i--) 73 { 74 for (j=1; j<=m; j++) ans=min(ans,f[i][j]); //若 ans 有值则代表能到达。 75 if (ans < inf) break; 76 if (da[i].up <= m) cnt--; // da[i].up <= m 才是真水管 77 } 78 if (cnt == q) printf("1\n%d\n",ans); 79 else printf("0\n%d\n",cnt); 80 return 0; 81 }