1. 程式人生 > >【NOIP2014】飛揚的小鳥

【NOIP2014】飛揚的小鳥

兩個 names 新解 char 循環 判斷 單位 output 過多

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

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

  1. 遊戲界面是一個長為 nn,高為 mm 的二維平面,其中有 kk 個管道(忽略管道的寬度)。
  2. 小鳥始終在遊戲界面內移動。小鳥從遊戲界面最左邊任意整數高度位置出發,到達遊戲界面最右邊時,遊戲完成。
  3. 小鳥每個單位時間沿橫坐標方向右移的距離為 11,豎直移動的距離由玩家控制。如果點擊屏幕,小鳥就會上升一定高度 XX,每個單位時間可以點擊多次,效果疊加;如果不點擊屏幕,小鳥就會下降一定高度
    YY。小鳥位於橫坐標方向不同位置時,上升的高度 XX 和下降的高度 YY 可能互不相同。
  4. 小鳥高度等於 00 或者小鳥碰到管道時,遊戲失敗。小鳥高度為 mm 時,無法再上升。

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

輸入格式

11 行有 33 個整數 n,m,kn,m,k,分別表示遊戲界面的長度,高度和水管的數量,每兩個整數之間用一個空格隔開;

接下來的 nn 行,每行 22 個用一個空格隔開的整數 XX 和 YY,依次表示在橫坐標位置 0n?10~n?1 上玩家點擊屏幕後,小鳥在下一位置上升的高度 XX,以及在這個位置上玩家不點擊屏幕時,小鳥在下一位置下降的高度

YY。

接下來 kk 行,每行 33 個整數 P,L,HP,L,H,每兩個整數之間用一個空格隔開。每行表示一個管道,其中 PP 表示管道的橫坐標,LL 表示此管道縫隙的下邊沿高度,HH 表示管道縫隙上邊沿的高度(輸入數據保證 PP 各不相同,但不保證按照大小順序給出)。

輸出格式

共兩行。

第一行,包含一個整數,如果可以成功完成遊戲,則輸出 11,否則輸出 00。

第二行,包含一個整數,如果第一行為 11,則輸出成功完成遊戲需要最少點擊屏幕數,否則,輸出小鳥最多可以通過多少個管道縫隙。

樣例一

input

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

output

1
6

樣例二

input

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

output

0
3

限制與約定

對於 30%的數據:5n10,5m10,k=05≤n≤10,5≤m≤10,k=0,保證存在一組最優解使得同一單位時間最多點擊屏幕 33 次;

對於 50%的數據:5n20,5m105≤n≤20,5≤m≤10,保證存在一組最優解使得同一單位時間最多點擊屏幕 33 次;

對於 70%的數據:5n1000,5m1005≤n≤1000,5≤m≤100;

對於 100%的數據:5n10000,5m10000k<n,0<X<m,0<Y<m,0<P<n,0L<Hm,L+1<H5≤n≤10000,5≤m≤1000,0≤k<n,0<X<m,0<Y<m,0<P<n,0≤L<H≤m,L+1<H。

時間限制:1s1s

空間限制:128MB

首先想到設分f[i][j]表示到達地i行第j列所需要的最少點擊屏幕次數。轉移方程為

f[ i ][ j ]=min{f[ i-1 ][ j - k*x[i-1] ] + k} (1<= k <= j/x) 上升—— ①

f[ i ][ j ]=min{f[ i-1 ][ j + y[i-1] } ( j + y[i-1] <= m) 下降

顯然,下降可以O(1)轉移,主要問題在上升的轉移。

我們將上升的方程變一下:

f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ (j - x[i-1]) - (k-1)*x[i-1] ] + k -1} ——②

這是 f[ i ][ j - x[i-1] ] 的轉移。

由 ② 化簡可得:

f[ i ][ j - x[i-1] ]=min{f[ i-1 ][ j - k*x[ i-1] ] + k -1}

消去f[ i-1 ][ j - k*x[ i-1] ]

f[ i ][ j ]= f[ i ][ j - x[ i-1 ] ]+1

於是就可以O(n*m)的時間內出解

 1 #include <map>
 2 #include <set>
 3 #include <cmath>
 4 #include <ctime>
 5 #include <queue>
 6 #include <stack>
 7 #include <cstdio>
 8 #include <string>
 9 #include <vector>
10 #include <cstdlib>
11 #include <cstring>
12 #include <iostream>
13 #include <algorithm>
14 #define rg register
15 using namespace std;
16 #define ll long long
17 
18 inline int gi()
19 {
20     rg bool b=0;
21     rg int r=0;
22     char c=getchar();
23     while(c<0 || c>9)
24     {
25         if(c==-) b=!b;
26         c=getchar();
27     }
28     while(c>=0 && c<=9)
29     {
30         r=r*10+c-0;
31         c=getchar();
32     }
33     if(b) return -r;
34     return r;
35 }
36 
37 const int inf = 2100000000, N = 10005, M = 1005;
38 int n,m,q,x[N],y[N],f[N][M];
39 bool b[N];
40 struct data
41 {
42     int up,down;
43 } da[N];
44 
45 int main()
46 {
47     freopen ("birda.in","r",stdin);
48     freopen ("birda.out","w",stdout);
49     int i,p,j,k,cnt,ans;
50     n=gi(), m=gi(), q=gi();
51     for (i=0; i<n; i++) x[i]=gi(), y[i]=gi();
52     for (i=1; i<=n; i++) da[i].down=0, da[i].up=m+1;
53     for (i=0; i<q; i++) p=gi(), da[p].down=gi(), da[p].up=gi();    //一定要加,不然會影響到第65行的循環枚舉 
54     for (i=1; i<=n; i++) for (j=0; j<=m; j++) f[i][j]=inf;    //初始化。0位置除地面外都為0
55     f[0][0]=inf;
56     for (i=1; i<=n; i++)
57     {
58         for (j=x[i-1]; j<=m; j++)
59         {
60             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);  //更新解,先不考慮水管 
61             if (j == m)        //特殊判斷 j==m 的情況,因為不能超過 m ,所以有多種轉移 
62                 for (k=m-x[i-1]; k<=m; k++)
63                     f[i][j]=min(f[i][j],f[i-1][k]+1), f[i][j]=min(f[i][j],f[i][k]+1);
64         }
65         for (j=da[i].down+1; j<da[i].up; j++)  //處理下落,必須是合法的 
66             if (j+y[i-1] <= m)
67                 f[i][j]=min(f[i][j],f[i-1][j+y[i-1]]);
68         for (j=1; j<=da[i].down; j++) f[i][j]=inf;  //考慮水管,去掉不合法的解 
69         for (j=m; j>=da[i].up; j--) f[i][j]=inf;
70     }
71     cnt=q,ans=inf;
72     for (i=n; i>=1; i--)
73     {
74         for (j=1; j<=m; j++) ans=min(ans,f[i][j]);  //若 ans 有值則代表能到達。 
75         if (ans < inf) break;
76         if (da[i].up <= m) cnt--;  //  da[i].up <= m 才是真水管 
77     }
78     if (cnt == q) printf("1\n%d\n",ans);
79     else printf("0\n%d\n",cnt);
80     return 0;
81 }

【NOIP2014】飛揚的小鳥