P1941 [NOIP2014]飞扬的小鸟-动态规划,模拟

版权声明:原创,勿转 https://blog.csdn.net/qq_42386465/article/details/82829459

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

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

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

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

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

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

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

https://www.luogu.org/problemnew/show/P1941

很好想到f[i][j]表示走到(i,j)所需的最小步数。

同时很好想到状态转移方程:

f[i][j]=min(f[i][j],f[i-1][j-p*x[i-1]]+p),p>=1(因为在一个点可以再单位时间内点p次)

或者

f[i][j]=min(f[i][j],f[i-1][j+y[i-1]])(这表示自然下落)

以上很简单,但考虑的太少了

还要考虑到顶到顶了即j==m时的情况

来一个循环for(int z=m-x[i-1];z<=m;z++)

f[i][j]=min(f[i][j],f[i-1][z]+1)(走1步到顶)

f[i][j]=min(f[i][j],f[i][z]+1)(走2步或更多步到顶)

一个是i-1另一个是i,这个思想很重要。

也就是说你可以从这一行的下面转移到上面;

这个思想同样可以应用到之前那个式子的优化!!!

这个优化很重要!!

f[i][j]=min(f[i][j],f[i-1][j-p*x[i-1]]+p)

想一想,从f[3][1]到f[4][4]或者从f[3][1]到f[4][7],x[3]=3;

第一个按了一下,第二个按了两下;

是不是就等价于按一下从f[3][1]到f[4][4],再按一下从f[4][4]到f[4][7];

这样就得到了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);

省去了p的枚举!!

至此此题再无难点~

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 100000
using namespace std;
const int N=10001;
const int M=1001;
int inline read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,k,id;
int x[N],y[N];
int f[N][M];
int l[N],h[N];
bool p[N];
int main()
{
    n=read();m=read();k=read();
    for(int i=0;i<=n-1;i++){
        x[i]=read();y[i]=read();
        l[i]=0;h[i]=m+1;
    }
    l[n]=0;h[n]=m+1;
    for(int i=1;i<=k;i++){
        id=read();l[id]=read();h[id]=read();
        p[id]=true;
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<=m;j++)
        {
            f[i][j]=inf;
        }
    }
    f[0][0]=inf;
    for(int i=1;i<=m;i++){
        f[0][i]=0;
    }
    for(int i=1;i<=n;i++){
        for(int j=x[i-1];j<=m;j++){
            if(j==m){
                for(int z=m-x[i-1];z<=m;z++){
                    f[i][j]=min(f[i][j],f[i-1][z]+1);
                    f[i][j]=min(f[i][j],f[i][z]+1);
                }
            }
            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);
            
        }
        for(int j=max(1,l[i]+1);j<=min(m-y[i-1],h[i]-1);j++){
            f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);
        }
        for(int j=l[i];j>=1;j--){
            f[i][j]=inf;
        }
        for(int j=h[i];j<=m;j++){
            f[i][j]=inf;
        }
    }
    int ans=inf;
    int cnt=k;
    for(int i=n;i>=1;i--){
        for(int j=l[i]+1;j<=h[i]-1;j++){
            ans=min(ans,f[i][j]);
        }
        if(ans<inf){
            break;
        }
        if(p[i]==true){
            k--;
        }
    }
    if(cnt==k){
        cout<<1<<endl<<ans;
    }
    else{
        cout<<0<<endl<<k;
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42386465/article/details/82829459
今日推荐