1. 程式人生 > >P1941 飛揚的小鳥-動態規劃,模擬

P1941 飛揚的小鳥-動態規劃,模擬

Flappy Bird是一款風靡一時的休閒手機遊戲。玩家需要不斷控制點選手機螢幕的頻率來調節小鳥的飛行高度,讓小鳥順利通過畫面右方的管道縫隙。如果小鳥一不小心撞到了水管或者掉在地上的話,便宣告失敗。

為了簡化問題,我們對遊戲規則進行了簡化和改編:

遊戲介面是一個長為 n,高為 m 的二維平面,其中有 k 個管道(忽略管道的寬度)。

小鳥始終在遊戲介面內移動。小鳥從遊戲介面最左邊任意整數高度位置出發,到達遊戲介面最右邊時,遊戲完成。

小鳥每個單位時間沿橫座標方向右移的距離為 1,豎直移動的距離由玩家控制。如果點選螢幕,小鳥就會上升一定高度 X,每個單位時間可以點選多次,效果疊加;如果不點選螢幕,小鳥就會下降一定高度 Y。小鳥位於橫座標方向不同位置時,上升的高度 X 和下降的高度 Y 可能互不相同。

小鳥高度等於 0 或者小鳥碰到管道時,遊戲失敗。小鳥高度為 m 時,無法再上升。

現在,請你判斷是否可以完成遊戲。如果可以,輸出最少點選螢幕數;否則,輸出小鳥最多可以通過多少個管道縫隙。

很好想到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;
}