飞扬的小鸟

题目描述

Flappy Bird 是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。

为了简化问题,我们对游戏规则进行了简化和改编:

1.  游戏界面是一个长为n,高 为m的二维平面,其中有k个管道(忽略管道的宽度)。

2.  小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。

3.  小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上升的高度X和下降的高度Y可能互不相同。

4.  小鸟高度等于0或者小鸟碰到管道时,游 戏 失 败 。小 鸟 高 度 为m时,无法再上升。

现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙。

输入格式

输入文件名为 bird.in。

第1行有3个整数n,m,k,分别表示游戏界面的长度,高度和水管的数量,每两个整数之间用一个空格隔开;

接下来的n行,每行2个用一个空格隔开的整数X和Y,依次表示在横坐标位置0~n-1上玩家点击屏幕后,小鸟在下一位置上升的高度X,以及在这个位置上玩家不点击屏幕时,小鸟在下一位置下降的高度Y。

接下来k行,每行3个整数P,L,H,每两个整数之间用一个空格隔开。每行表示一个管道,其中P表示管道的横坐标,L表示此管道缝隙的下边沿高度为L,H表示管道缝隙上边沿的高度(输入数据保证P各不相同,但不保证按照大小顺序给出)。

输出格式

输出文件名为bird.out。

共两行。

第一行,包含一个整数,如果可以成功完成游戏,则输出1,否则输出0。

第二行,包含一个整数,如果第一行为1,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。

输入输出样例

输入样例1:
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 
输出样例1:
1
6
输入样例2:
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 
输出样例2:
0
3

说明

【输入输出样例说明】

如下图所示,蓝色直线表示小鸟的飞行轨迹,红色直线表示管道。

【数据范围】

对于30% 的数据:5 ≤ n ≤ 10,5 ≤ m ≤ 10,k = 0 ,保证存在一组最优解使得同一单位时间最多点击屏幕3 次;

对于50% 的数据:5 ≤ n ≤ 2 0 ,5 ≤ m ≤ 10,保证存在一组最优解使得同一单位时间最多点击屏幕3 次;

对于70% 的数据:5 ≤ n ≤ 1000,5 ≤ m ≤ 1 0 0 ;

对于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< H 。

【耗时限制】1000ms 【内存限制】128MB


定义状态:d[i][j]表示到达(i, j)这个位置所需要的最少移动次数,ans = min(d[n][j]); (1 <=j <=m)

状态转移方程:考虑是如何到达(i, j)这个位置的:

① 在i-1时没有做点击操作,下降过来的:d[i][j] = d[i-1][j + y[i-1]];

② 在i-1时点击了1次上升过来的:d[i][j] = d[i-1][j - x[i-1]] + 1;

③ 在i-1时点击了2次上升过来的:d[i][j] = d[i-1][j - 2 * x[i-1]] + 2;

④ 在i-1时点击了k次上升过来的:d[i][j] = d[i-1][j - k * x[i-1]] + k;

将上述决策分为三类:

① 下降过来:d[i][j] = d[i-1][j + y[i-1]];

② 点击1次过来的(类似完全背包):d[i][j] = d[i-1][j - x[i-1]] + 1

;③ 点击至少2次过来的(类似完全背包):d[i][j] = d[i][j-x[i-1]] + 1(d[i][j-x[i-1]]由上升得来);

注意:只点击1次的情况不能通过式子③推导,因为1次不能通过0次转换过来,0次就是下降,下降和上升是互斥的,不可能下降后再上升。

为防止出现先下降和上升的情况,从而导致上升依赖了下降,所以上升和下降要分开处理,先处理上升,再处理下降

细节1:对于任意横向位置i,如果该位置为水管,则水管覆盖的区域无法到达,设置为INF。

细节2:如果一次跳跃会超过m,不代表不可以跳跃,只是跳跃后的高度是m。j=m时,可以通过区间[j-x[i-1], m]转换而来。

细节3:d[i][j]依赖d[i][j-x[i-1]]时,如果j-x[i-1]是管道,也需要先计算。否则会影响d[i][j]的计算,当所有d[i][j](1<=j<=m)都计算完后,再将管道处重新设置为INF。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=10005;
int n,m,k;
int p[N],l[N],h[N],dp[N][1010],up[N],down[N],f[N];
int ans1=0x3f3f3f3f;
int ans2=0x3f3f3f3f;
int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=0;i<n;++i){
        scanf("%d%d",&up[i],&down[i]);
        l[i]=0;
        h[i]=m+1;
    }
    l[n]=0;
    h[n]=m+1;
    for(int i=1;i<=k;++i){
        int x;
        scanf("%d",&x);
        scanf("%d%d",&l[x],&h[x]);
        l[x]++;h[x]--;p[x]=1;
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i=1;i<=m;i++)dp[0][i]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            if(j==m){
                for(int k=m-up[i-1];k<=m;k++){
                    dp[i][j]=min(dp[i][j],dp[i-1][k]+1);
                    dp[i][j]=min(dp[i][j],dp[i][k]+1);
                }
            }
            if(j-up[i-1]>0){
                dp[i][j]=min(dp[i][j],dp[i-1][j-up[i-1]]+1);
                dp[i][j]=min(dp[i][j],dp[i][j-up[i-1]]+1);
            }
        }
        //上升
        for(int j=1;j<=l[i]-1;++j)dp[i][j]=0x3f3f3f3f;
        for(int j=h[i]+1;j<=m;++j)dp[i][j]=0x3f3f3f3f;
        //把管子搞掉
        for(int j=1;j<=m;j++)
            if(j>=l[i]&&j<=h[i]&&j+down[i-1]<=m)
                dp[i][j]=min(dp[i][j],dp[i-1][j+down[i-1]]);
        //下降
    }
    for(int i=n;i>=1;i--){
        for(int j=l[i];j<=h[i];j++)ans2=min(ans2,dp[i][j]);
        if(ans2<0x3f3f3f3f)break;
        if(p[i])k--;
    }
    for(int i=1;i<=m;i++)ans1=min(ans1,dp[n][i]);
    if(ans1!=0x3f3f3f3f)printf("1\n%d",ans1);
    else printf("0\n%d",k);
    return 0;
}   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值