zoukankan      html  css  js  c++  java
  • luogu1941 飞扬的小鸟

    题目

    题目描述

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

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

    1. 游戏界面是一个长为n ,高为 m 的二维平面,其中有k 个管道(忽略管道的宽度)。

    2. 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。

    3. 小鸟每个单位时间沿横坐标方向右移的距离为1 ,竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度X ,每个单位时间可以点击多次,效果叠加;

    如果不点击屏幕,小鸟就会下降一定高度Y 。小鸟位于横坐标方向不同位置时,上升的高度X 和下降的高度Y 可能互不相同。

    1. 小鸟高度等于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:
    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:
    1
    6
    
    
    输入样例#2:
    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  
    输出样例#2:
    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 。

    分析

    这道题很容易想到是背包,难点大概就是各种状态的判断

    根据题目,可以将小鸟的飞行分为上升和下降两个部分分别判断,也就是分成完全背包和01背包两部分

    上升:path [ i ][ j ],表示当前由上升到达j高度所需最少点击数,一个时间可多次点击,所以当成完全背包

    下降:drop [ i ][ j ],表示当前由下降到达j高度所需最少点击数,因为一个单位时间只能下降一次,故用当成01背包方法

    用了背包的迭代求解,只是注意需要保留上一个单位时间的数据

    一个关于顶层的特判,详见代码

    代码

      1 /*
      2 ID:Mandy
      3 language:c++
      4 problem:luogu1941 bird
      5 */
      6 #include<bits/stdc++.h>
      7 
      8 #define M 1002
      9 #define N 10002
     10 #define Max(x,y) (x)>(y)?(x):(y)
     11 #define Min(x,y) (x)<(y)?(x):(y)
     12 #define up(i,l,r) for(int i=l;i<=r;++i)
     13 #define down(i,l,r) for(int i=r;i>=l;--i)
     14 
     15 using namespace std;
     16 
     17 int n,m,k,path[3][M],drop[3][M];//滚动数组存背包 
     18 //path记录小鸟当前上升的路线最少次数,drop是当前下降的最少点击次数 
     19 struct Conduit     
     20 {
     21     int pos,ovr,bot;
     22 }cdt[M];//管道  pos-位置,ovr-管道缝隙的上边沿高度,bot-管道缝隙的下边沿高度 
     23 struct Rule 
     24 {
     25     int inc,dec;
     26 }rule[N];//飞翔规则 inc-上升高度,dec-下降高度 
     27 
     28 inline int read()
     29 {
     30     int x=0; char c=getchar();
     31     while(c<'0'||c>'9') c=getchar();
     32     while(c>='0'&&c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
     33     return x;
     34 }
     35 
     36 inline void put(int x)
     37 {
     38     if(x==0) {putchar('0'); return;}
     39     int num=0; char c[15];
     40     while(x){c[++num]=(x%10)^48; x/=10;}
     41     while(num) putchar(c[num--]);
     42 }
     43 //输入输出优化 
     44 void docu()
     45 {
     46     freopen("bird.in","r",stdin);
     47 //  freopen("bird.out","w",stdout);
     48 }
     49 
     50 bool cmp(Conduit a,Conduit b)
     51 {
     52     return a.pos<b.pos;
     53 }
     54 
     55 void readdata()
     56 {
     57     n=read();
     58     m=read();
     59     k=read();
     60     up(i,1,n)
     61     {
     62         rule[i].inc=read();
     63         rule[i].dec=read();
     64     }
     65     up(i,1,k)
     66     {
     67         cdt[i].pos=read();
     68         cdt[i].bot=read();
     69         cdt[i].ovr=read();
     70     }
     71     sort(cdt+1,cdt+k+1,cmp);
     72     //注意输入数据并没有按管道从前往后的顺序 ,所以需要排序 
     73 }
     74 
     75 void work()
     76 {
     77     int minans=1<<30;
     78     int num=1,now,pre;//num是指当前还未经过的管道的编号 
     79     int preu=1;
     80     int prev=m;//preu-上次能飞到的最低位置,prev-上次能飞到的最高位置
     81     int u=1,v=m;//u-能飞到的最低位置,v- 能飞到的最高位置
     82     up(i,1,n)
     83     {
     84         now=i&1;//now指当前在背包中的位置 
     85         pre=1-now;//指上一个单位时间在背包中的位置 
     86         bool judge=0;
     87         preu=u;
     88         prev=v;
     89         u=1,v=m;
     90         if(cdt[num].pos==i)//如果当前位置有管道 
     91         {
     92             u=cdt[num].bot+1;
     93             v=cdt[num].ovr-1;//管道占了的位置不能飞了 ,u是下限,v是上限 
     94             num++;
     95         }
     96         if(v==m)//如果可以到达顶层 
     97         {
     98             if(m==prev) path[now][m]=path[pre][m]+1;//如果上一个单位时间也可以到达顶层
     99             else path[now][m]=0x3f3f3f3f;
    100         }
    101 
    102         up(j,1,m)
    103         {
    104             if(j!=m) path[now][j]=0x3f3f3f3f;
    105             drop[now][j]=0x3f3f3f3f;//初始化 
    106 
    107             //上升 因为完全背包要覆盖叠加,所以从1到m都要算 
    108             int ht=j-rule[i].inc;//ht是指能到达j高度的上一单位时间的高度 
    109             if(j>v||j<u)//如果这次不能到达j高度 这里的值用于叠加 
    110             {   
    111                 if(ht>=preu)
    112                 {
    113                     if(ht<=prev) path[now][j]=Min(path[now][ht]+1,Min(path[pre][ht]+1,drop[pre][ht]+1));
    114                     else path[now][j]=Min(path[now][ht]+1,path[now][j]);
    115                 }
    116             }
    117             else
    118             {
    119                 if(ht>=preu&&ht<=prev) path[now][j]=Min(Min(path[now][j],drop[pre][ht]+1),path[pre][ht]+1);
    120                 if(ht>=1) path[now][j]=Min(path[now][j],path[now][ht]+1);//注意一维与二维的条件要相符 
    121             }
    122 
    123             ht=j+rule[i].dec;//下落 下落不能叠加或上升,故特判 
    124             if(ht<=prev&&ht>=preu&&j<=v&&j>=u) drop[now][j]=Min(path[pre][ht],drop[pre][ht]);
    125 
    126             if(v==m)//顶层特判 
    127             {
    128 
    129                 int q=(m-j)/rule[i].inc;
    130                 if(q*rule[i].inc!=m-j||j==m) q++;
    131 
    132                 if(j<=prev&&j>=preu) path[now][m]=Min(path[now][m],Min(drop[pre][j]+q,path[pre][j]+q));
    133                 path[now][m]=Min(path[now][m],path[now][j]+q);
    134             }
    135 
    136             if(path[now][j]<0x3f3f3f3f||drop[now][j]<0x3f3f3f3f) judge=1;//如果可以通过 
    137         }
    138         if(judge==0)//如果不能通过 
    139         {
    140             putchar('0');
    141             putchar('
    ');
    142             put(num-2);//num记录当前未到达的管道的编号,如果当前到达的管道不能通过,通过的管道应有num-2个 
    143             return;
    144         }
    145     }
    146     putchar('1');
    147     putchar('
    ');
    148     up(i,u,v) minans=Min(minans,Min(path[now][i],drop[now][i]));//注意drop不要忘了 
    149     put(minans);
    150 }
    
    int main()
    {
    //  docu();
        readdata();//读入数据 
        work();
        return 0;
    }
    非做顽石不可,哪管他敬仰暗唾
  • 相关阅读:
    KafkaZookeeper1-整体介绍
    spark thrift server configuration
    Spark Streaming 总结
    SparkSession
    Spark SQL
    Kafka Consumer2
    Kafka Consumer1
    Storm Spout
    java Future && Guava Future
    基本命令
  • 原文地址:https://www.cnblogs.com/Mandy-H-Y/p/11354111.html
Copyright © 2011-2022 走看看