題目描述算法
Flappy Bird是一款風靡一時的休閒手機遊戲。玩家須要不斷控制點擊手機屏幕的頻率來調節小鳥的飛行高度,讓小鳥順利經過畫面右方的管道縫隙。若是小鳥一不當心撞到了水管或者掉在地上的話,便宣告失敗。app
爲了簡化問題,咱們對遊戲規則進行了簡化和改編:spa
遊戲界面是一個長爲 n,高爲 m 的二維平面,其中有 k 個管道(忽略管道的寬度)。code
小鳥始終在遊戲界面內移動。小鳥從遊戲界面最左邊任意整數高度位置出發,到達遊戲界面最右邊時,遊戲完成。blog
小鳥每一個單位時間沿橫座標方向右移的距離爲 1,豎直移動的距離由玩家控制。若是點擊屏幕,小鳥就會上升必定高度 X,每一個單位時間能夠點擊屢次,效果疊加;若是不點擊屏幕,小鳥就會降低必定高度 Y。小鳥位於橫座標方向不一樣位置時,上升的高度 X 和降低的高度 Y 可能互不相同。遊戲
小鳥高度等於 0 或者小鳥碰到管道時,遊戲失敗。小鳥高度爲 m 時,沒法再上升。get
如今,請你判斷是否能夠完成遊戲。若是能夠,輸出最少點擊屏幕數;不然,輸出小鳥最多能夠經過多少個管道縫隙。string
輸入格式it
第 11 行有 33 個整數 n,m,k,分別表示遊戲界面的長度,高度和水管的數量,每兩個整數之間用一個空格隔開;io
接下來的 n 行,每行 2 個用一個空格隔開的整數 X 和 Y,依次表示在橫座標位置 0∼n−1 。上玩家點擊屏幕後,小鳥在下一位置上升的高度 X,以及在這個位置上玩家不點擊屏幕時,小鳥在下一位置降低的高度 Y。
接下來 k 行,每行 3 個整數 P,L,H,每兩個整數之間用一個空格隔開。每行表示一個管道,其中 P 表示管道的橫座標,L 表示此管道縫隙的下邊沿高度,H 表示管道縫隙上邊沿的高度(輸入數據保證 P 各不相同,但不保證按照大小順序給出)。
輸出格式
共兩行。
第一行,包含一個整數,若是能夠成功完成遊戲,則輸出 1,不然輸出 0。
第二行,包含一個整數,若是第一行爲 1,則輸出成功完成遊戲須要最少點擊屏幕數,不然,輸出小鳥最多能夠經過多少個管道縫隙。
數據範圍
n<=10000,m<=1000
題目分析
^-^
首先讀題:(重點)
每一個單位時間能夠點擊屢次,效果疊加
——有多種狀況,這是一個徹底揹包
若是不點擊屏幕,小鳥就會降低必定高度
——只有一種狀況,這是一個01揹包
小鳥高度爲 m 時,沒法再上升
——須要特判
分析:
f[i][j]表示跳到當前位置的最少次數
rise[i]表示點一下上升的高度
fall[i]表示不點降低的高度
首先畫一張圖,
咱們能夠發現
f[i][j]的位置可能從
f[i-1][j+fall[i-1]]滑下來
或者從f[i-1][j-t*rise[i-1]]跳過來
(1<=j-t*rise[i-1]<=m)
從f[i-1][j+1]滑下來很好解決,01揹包嘛
代碼以下
for(j=1;j<=m-fall[i-1];j++) f[i][j]=min(f[i][j],f[i-1][j+fall[i-1]]);
可是從f[i-1][j-t*rise[i-1]]跳過來就很差辦了
若是每次都直接枚舉全部j-t*rise[i-1]的話,
根據題目數據範圍可推出(n<=10000,m<=1000)
最壞有超過50億次運算
確定會超時#-_-
可是畢竟這是一個徹底揹包啊
有沒有其餘方法呢
咱們能夠用徹底揹包的思路
j從小到大增長
每次將f[i][j-rise[i-1]]和f[i-1][j-rise[i-1]]的狀態轉移過來
由於j是從小到大的,
因此f[i][j-rise[i-1]]實際上是轉移的
f[i-1][j-rise[i-1]*t]和f[i-1][j-rise[i-1]*2]的狀態
無論它轉移的是那個位置過來的
f[i][j-rise[i-1]]已經是當前位置的最優
這樣就能夠實如今同一時間點裏屢次點擊屏幕
代碼以下
for(j=rise[i-1]+1;j<=rise[i-1]+m;j++) f[i][j]=min(f[i-1][j-rise[i-1]]+1,f[i][j-rise[i-1]]+1);
算法的正確性
因而我就滿心歡喜地交了上去^-^
for(j=1;j<=m-fall[i-1];j++) f[i][j]=min(f[i][j],f[i-1][j+fall[i-1]]); for(j=rise[i-1]+1;j<=rise[i-1]+m;j++) f[i][j]=min(f[i-1][j-rise[i-1]]+1,f[i][j-rise[i-1]]+1);
(⊙o⊙)
第18點是來幹什麼的
什麼,竟(dang)然WA了
首先,
管道沒有特殊處理,
在處理完當前狀態後
要將管道位置的數值賦值爲極大值
其次,
向上跳躍的處理要在向下滑落的處理以前
由於同一時間單位內
在滑落以後不能繼續跳躍
第三,
到達屏幕頂端的位置要特殊處理
最後,
f[i][0]要賦值爲0
由於開始小鳥能夠在任何位置
終於......
AC代碼
#include<cstdio> #include<cstring> #include<algorithm> #define infi 0x7fffffff #define N 10000 #define M 1000 using namespace std; int n,m,k; int rise[N+2],fall[N+2]; int f[N+2][M*2+2]; int l[N+2],h[N+2]; bool a[N+2];//a[i]表示橫座標爲i的位置是否有管道 template<typename T>inline void r(T& x){ char temp=getchar();bool u=0; for(x=0;temp<'0'||temp>'9';u=temp=='-',temp=getchar()); for(;temp>='0'&&temp<='9';x=x*10+temp-'0',temp=getchar()); if(u)x=-x; return ; }//快讀 int main(){ register int i,j,ans=infi; memset(f,127,sizeof f); memset(a,0,sizeof a); r(n),r(m),r(k); for(i=1;i<=n;i++) l[i]=0,h[i]=m+1; { register int p; for(i=0;i<n;i++)r(rise[i]),r(fall[i]); for(i=1;i<=k;i++) r(p),r(l[p]),r(h[p]),a[p]=1; } for(i=1;i<=m;i++) f[0][i]=0; for(i=1;i<=n;i++){ for(j=rise[i-1]+1;j<=rise[i-1]+m;j++)//跳躍 f[i][j]=min(f[i-1][j-rise[i-1]]+1,f[i][j-rise[i-1]]+1); //j<=rise[i-1]+m:再往上跳沒有意義 for(j=m+1;j<=rise[i-1]+m;j++) //頂端特殊處理 f[i][m]=min(f[i][m],f[i][j]); for(j=1;j<=m-fall[i-1];j++) //下滑 f[i][j]=min(f[i][j],f[i-1][j+fall[i-1]]); for(j=1;j<=l[i];j++) //管道賦值爲極大值 f[i][j]=2e9; for(j=h[i];j<=m;j++) f[i][j]=2e9; } for(i=1;i<=m;i++)ans=min(ans,f[n][i]); if(ans<2e9)printf("1\n%d\n",ans); else{ register int flag=0; for(i=n-1;i>=1&&!flag;i--) for(j=1;j<=m&&!flag;j++) if(f[i][j]<2e9){ flag=0; flag=i; } ans=0; for(i=1;i<=flag;i++) if(a[i])ans++; printf("0\n%d\n",ans); } return 0; } //碼醜勿噴^-^