NOIP2014 飞扬的小鸟

来源:互联网 发布:ps怎么制作淘宝首页 编辑:程序博客网 时间:2024/05/01 20:43
3. 飞扬的小鸟

(bird.cpp/c/pas)

【问题述】

Flappy Bird 是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。

为了简化问题,我们对游戏规则进行了简化和改编:

  1. 游戏界面是一个长为n,高 为m的二维平面,其中有k个管道(忽略管道的宽度)。
  2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。
  3. 小鸟每个单位时间沿横坐标方向右移的距离为1,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X,每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度Y。小鸟位于横坐标方向不同位置时,上升的高度X和下降的高度Y可能互不相同。
  4. 小鸟高度等于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,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。

 

【输入输出样例1】

bird.in

bird.out

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

 

【输入输出样例2】

bird.in

bird.out

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≤20,5≤m≤10,保证存在一组最优解使得同一单位时间最多点击屏幕3次;

对于70%的数据:5≤n≤1000,5≤m≤100;

对于100%的数据:5≤n≤10000,5≤m≤1000,0≤k<n,0<X<m,0<Y<m,0<P<n,0≤L<H ≤m,L+1<H。

 

 

【思路】

 

  题目思路很简单,但是写起来总感觉不顺手。

  状态转移方程:

   D[i][j]=min{d[i-1][j+y[i-1], d[i-1][j-k*x[i-1]+k}

  这里的k容易联想到完全背包的无限物品,类似的,不用去枚举k,改成d[i][j-x[i]]+1,它的意思其实就是累计。修改之后还需要注意由y划分子问题应该放在x之后,否则会造成决策叠加的效果。

J==m的特殊情况:注意到题目中说“小 鸟 高 度 为m时,无法再上升。”特殊处理:也是用累计的方法,d[i][j]=min(d[i][j],d[i][pos]+1) pos from [j-x[i-1],m] 由点成为了线段。

  Fly失败:用一个flag,只要d[i][]全都为INF则问题不能求解,相应输出结束。

 

【代码】

 1 #include<iostream> 2 #define FOR(a,b,c) for(int a=(b);a<(c);a++) 3 using namespace std; 4  5 const int INF=1<<30; 6 const int maxn = 10000 + 5, maxm = 1000+5;  7 int n,m,K; 8 int d[maxn][maxm]; 9 int x[maxn],y[maxn],up[maxn],down[maxn];10 11 int main() {12     ios::sync_with_stdio(false);13     14     cin>>n>>m>>K;15     FOR(i,0,n) {16       cin>>x[i]>>y[i]; 17       up[i]=m+1; down[i]=0;   //down=0 up=m+1 18     } up[n]=m+1; down[n]=0;19     20     FOR(i,0,K) {21         int p; cin>>p;22         cin>>down[p]>>up[p];23     }24     FOR(i,1,n+1) FOR(j,0,m+1) d[i][j]=INF;25     d[0][0]=INF;//d声明在全局变量自动清0 26     27     FOR(i,1,n+1)  //i从1开始 到n结束 28       {29           bool flag=true; 30         FOR(j,down[i]+1,up[i]) {  //这里写错过== 31           if(j-x[i-1]>down[i-1]) {32            d[i][j]=min(d[i][j],d[i][j-x[i-1]]+1);33            d[i][j]=min(d[i][j],d[i-1][j-x[i-1]]+1);34            }35            if(j==m) FOR(pos,j-x[i-1],j+1) {  //特殊情况特殊判断//注意不要写在上一个if里 36              d[i][j]=min(d[i][j],d[i][pos]+1);37              d[i][j]=min(d[i][j],d[i-1][pos]+1);38            } 39            if(d[i][j]<INF) flag=false;40         }41         //求完关于x之后才能求y 否则x中的决策d[i][j-x[i]]可能会转移到y中求过的状态 造成既下降又上升的情况 42         FOR(j,down[i]+1,up[i]) if(j+y[i-1]<up[i-1]) { 43          d[i][j]=min(d[i][j],d[i-1][j+y[i-1]]);44          if(d[i][j]<INF) flag=false;45         } 46 47         if(flag) { //如果本i不能通过那么output退出 48             int sum=0;49             FOR(k,0,i) 50                 if(up[k]!=m+1 || down[k]!=0) sum++;  //如果是管道 //||而非&& 51          cout<<0<<"\n"<<sum; 52          return 0;53        }54       }55       56     int mini=INF;57     FOR(j,1,m+1) mini=min(mini,d[n][j]);  //最右边为n 58     cout<<1<<"\n"<<mini;59     return 0;60     61 }

 

0 0
原创粉丝点击