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;
}