題目描述
Flappy Bird 是一款風靡一時的休閑手機游戲。玩家需要不斷控制點擊手機屏幕的頻率來調節小鳥的飛行高度,讓小鳥順利通過畫面右方的管道縫隙。如果小鳥一不小心撞到了水管或者掉在地上的話,便宣告失敗。
為了簡化問題,我們對游戲規則進行了簡化和改編:
-
游戲界面是一個長為n ,高為 m 的二維平面,其中有k 個管道(忽略管道的寬度)。
-
小鳥始終在游戲界面內移動。小鳥從游戲界面最左邊任意整數高度位置出發,到達游戲界面最右邊時,游戲完成。
- 小鳥每個單位時間沿橫坐標方向右移的距離為1 ,豎直移動的距離由玩家控制。如果點擊屏幕,小鳥就會上升一定高度X ,每個單位時間可以點擊多次,效果疊加;
如果不點擊屏幕,小鳥就會下降一定高度Y 。小鳥位於橫坐標方向不同位置時,上升的高度X 和下降的高度Y 可能互不相同。
- 小鳥高度等於0 或者小鳥碰到管道時,游戲失敗。小鳥高度為 m 時,無法再上升。
現在,請你判斷是否可以完成游戲。如果可以 ,輸出最少點擊屏幕數;否則,輸出小鳥最多可以通過多少個管道縫隙。
輸入輸出格式
輸入格式:
輸入文件名為 bird.in 。
第1 行有3 個整數n ,m ,k ,分別表示游戲界面的長度,高度和水管的數量,每兩個
整數之間用一個空格隔開;
接下來的n 行,每行2 個用一個空格隔開的整數X 和Y ,依次表示在橫坐標位置0 ~n- 1
上玩家點擊屏幕后,小鳥在下一位置上升的高度X ,以及在這個位置上玩家不點擊屏幕時,
小鳥在下一位置下降的高度Y 。
接下來k 行,每行3 個整數P ,L ,H ,每兩個整數之間用一個空格隔開。每行表示一
個管道,其中P 表示管道的橫坐標,L 表示此管道縫隙的下邊沿高度為L ,H 表示管道縫隙
上邊沿的高度(輸入數據保證P 各不相同,但不保證按照大小順序給出)。
輸出格式:
輸出文件名為bird.out 。
共兩行。
第一行,包含一個整數,如果可以成功完成游戲,則輸出1 ,否則輸出0 。
第二行,包含一個整數,如果第一行為1 ,則輸出成功完成游戲需要最少點擊屏幕數,否則,輸出小鳥最多可以通過多少個管道縫隙。
輸入輸出樣例
10 10 6 3 9 9 9 1 2 1 3 1 2 1 1 2 1 2 1 1 6 2 2 1 2 7 5 1 5 6 3 5 7 5 8 8 7 9 9 1 3
1 6
10 10 4 1 2 3 1 2 2 1 8 1 8 3 2 2 1 2 1 2 2 1 2 1 0 2 6 7 9 9 1 4 3 8 10
0 3
說明
【輸入輸出樣例說明】
如下圖所示,藍色直線表示小鳥的飛行軌跡,紅色直線表示管道。
【數據范圍】
對於30% 的數據:5 ≤ n ≤ 10,5 ≤ m ≤ 10,k = 0 ,保證存在一組最優解使得同一單位時間最多點擊屏幕3 次;
對於50% 的數據:5 ≤ n ≤ 2 0 ,5 ≤ m ≤ 10,保證存在一組最優解使得同一單位時間最多點擊屏幕3 次;
對於70% 的數據:5 ≤ n ≤ 1000,5 ≤ m ≤ 1 0 0 ;
對於100%的數據:5 ≤ n ≤ 100 0 0 ,5 ≤ m ≤ 1 0 00,0 ≤ k < n ,0<X < m ,0<Y <m,0<P <n,0 ≤ L < H ≤ m ,L +1< H 。
分析:每次可以跳xi,可以跳k次,每次只能往上跳和往下掉一次,這難道是背包?而且還不是簡單的背包,完全背包+0-1背包?對.首先可以明確向上是完全背包,向下是0-1背包,但是不能同時向上和向下,必須要分開處理,可以參考守望者的逃離,具體為什么,是因為會造成決策混亂,因為f[i][j]可以從f[i][j-x[i]]得到,而f[i][j-x[i]]可能是由下落得到的,不能夠不點又點.那么先遞推向上的,首先f[i][j]可以從f[i-1][j-x[i]]得到,然后又可以通過f[i][j-x[i]]得到,那么遞推取最小值即可,不過注意到跳到m游戲不會結束,所以要從m-x[i]到m要繼續枚舉j來更新f[i][m],然后依據這個來更新向下的,柱子所覆蓋的全部賦值為inf,最后判斷一下就可以出答案了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 10010; const int inf = 1000000000; int n, m, k, p, ans,up[maxn],down[maxn],x[maxn],y[maxn],f[maxn][1010]; int main() { scanf("%d%d%d", &n, &m, &k); up[n] = m + 1; down[n] = 0; for (int i = 0; i < n; i++) { scanf("%d%d", &x[i], &y[i]); up[i] = m + 1; down[i] = 0; } for (int i = 1; i <= k; i++) { scanf("%d", &p); scanf("%d%d", &down[p], &up[p]); } 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 <= n; i++) { for (int j = x[i - 1];j <= m; j++) { 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 = m - x[i - 1]; j <= m; j++) { f[i][m] = min(f[i][m], f[i - 1][j] + 1); f[i][m] = min(f[i][m], f[i][j] + 1); } for (int j = down[i] + 1; j <= up[i] - 1; j++) if (j + y[i - 1] <= m) f[i][j] = min(f[i][j], f[i - 1][j + y[i - 1]]); for (int j = 1; j <= down[i]; j++) f[i][j] = inf; for (int j = up[i]; j <= m; j++) f[i][j] = inf; } ans = inf; int cnt = k; for (int i = n; i >= 1; i--){ for (int j = down[i] + 1; j <= up[i] - 1; j++) ans = min(ans, f[i][j]); if (ans != inf) break; if (up[i] != m + 1) cnt--; } if (cnt == k) printf("1\n%d\n", ans); else printf("0\n%d\n", cnt); return 0; }