# NOIP2014 飞翔的小鸟 【DP】

Description

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

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

  1. 游戏界面是一个长为 nn,高为 mm 的二维平面,其中有 kk 个管道(忽略管道的宽度)。
  2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
  3. 小鸟每个单位时间沿横坐标方向右移的距离为 11,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 XX,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 YY。小鸟位于横坐标方向不同位置时,上升的高度 XX 和下降的高度 YY 可能互不相同。
  4. 小鸟高度等于 00 或者小鸟碰到管道时,游戏失败。小鸟高度为 mm 时,无法再上升。

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

题解

定义 f[i][j] 表示飞到 (i,j) 的最优解,这样,很容易想到暴力转移:枚举前一秒点了几下屏幕。这样的复杂度是 O(n3) 的。考虑优化转移, f[i][j]=min(f[i1][jkup[i1]]+k);f[i][jup[i1]]=min(f[i1][j(k+1)up[i1]]+k) | k=1,2,3...... ,可以发现, f[i][j]=min(f[i1][jup[i1]],f[i][jup[i1]])+1 ;稍微注意一下边界就可以了。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#define maxn 10006
#define maxm 1006
using namespace std;
inline char nc(){
    static char buf[100000],*i=buf,*j=buf;
    return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
int n,m,K,ans,U[maxn],D[maxn],up[maxn],dn[maxn],f[maxn][maxm];
int main(){
    freopen("bird.in","r",stdin);
    freopen("bird.out","w",stdout);
    memset(D,0,sizeof(D));memset(U,63,sizeof(U));int INF=U[0];
    n=_read();m=_read();K=_read();
    for(int i=0;i<n;i++)up[i]=_read(),dn[i]=_read();
    for(int i=1,x;i<=K;i++)x=_read(),D[x]=_read(),U[x]=_read();
    memset(f,63,sizeof(f));
    for(int i=1;i<=m;i++)f[0][i]=0;
    for(int i=1;i<=n;i++){
        for(int j=up[i-1];j<=m;j++)f[i][j]=min(f[i][j-up[i-1]],f[i-1][j-up[i-1]])+1;
        for(int j=m-up[i-1];j<=m;j++)f[i][m]=min(f[i][m],min(f[i][j],f[i-1][j])+1);
        for(int j=1;j<=m-dn[i-1];j++)f[i][j]=min(f[i][j],f[i-1][j+dn[i-1]]);
        for(int j=1;j<=m;j++) if(j<=D[i]||j>=U[i])f[i][j]=INF;
    }
    ans=INF;
    for(int i=1;i<=m;i++)ans=min(ans,f[n][i]);
    if(ans==INF){
        printf("0\n");
        ans=0;
        for(int i=1;i<=n;i++)
         for(int j=1;j<=m;j++)if(f[i][j]<INF){ans=i;break;}
        int ans0=0;
        for(int i=1;i<=ans;i++) if(U[i]<INF)ans0++;
        printf("%d\n",ans0);
    }else printf("1\n%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值