noip2014 提高组题解 bird

来源:互联网 发布:算法工程师怎么考 编辑:程序博客网 时间:2024/06/05 15:18

题目描述:


                  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 ,则输出成功完成游戏需要最少点击屏幕数,否则,输出小鸟最多可以通过多少个管道缝隙。



解题思路:

首先在考场的时候看到这到题目脑子抽了,竟然没有想出来,然后出来后无聊时灵光一闪就想出来了(我TM真是。。。车啊。。。。。)

        

    说实话我不知道30分和50分应该怎么做,估计搜索吧。。。。。。。,这道题目需要对水管的横坐标进行排序我就不说了


    1、70分:

          很裸的dp,定义状态f[i][j]为小鸟飞到(i,j)时所需要的最小的步数,转移方程应该很好写,如果写不出我也没办法了,估计你没学过dp(好像广搜TMD也可以。。。。艹

     

     2、100分

          就是贴吧大神们说的下落做0,1背包,上升做无限背包(虽然当时我想出来正解是竟然不知道这TM是背包。。。。。果然我是蒟蒻。。。。。。。。。)

          我们需要一些数组,f[i][j]表示小鸟飞到(i,j)是所需要的最小的步数,k[i][j]表示从i-1点击屏幕上升过来需要的最小步数(即在转移k[i][j]是必须要从f[i-1][t],(t<j)转移过来)

 如果用change[i].up表示从i-1到i点击一下屏幕的上升的高度,change[i].down表示从i-1到i下落的高度(边界条件请读者自己考虑)

         那么f[i][j]=MIN(f[i-1][j-change[i].up]+1,  k[i][j-change[i].up]+1,  f[i-1][j+change[i].down])  (转移时不需要考虑(i,j)是否是管子,只需要考虑转移过来的点是不是管子)

         f[i-1][j-change[i].up],  k[i][j-change[i].up],  f[i-1][j+change[i].down]中没意义的就不用考虑了

         对于 f[i-1][j-change[i].up]  来说,没有意义就是达不到i-1,j-change[i].up或者(i-1,j-change[i].up)是水管

         对于k[i][j-change[i].up]来说,没有意义就是在i-1通过点击屏幕上升达不到i,j-change[i].up

         对于f[i-1][j+change[i].down]来说,没有意义就是达不到(i-1,j+change[i].down)或者i-1,j+change[i].down)是水管

         这样我们就可以将时间复杂度降到O(nm),AC妥妥的。。。。。。

         那么如何判断无解呢?很简单,当i为某水管的横坐标时,只需要判断是不是对于所在管子的L,H间的存在一个点使得小鸟可以飞到,如果不存在就直接输出(当前管子编号-1)

         证明我就不啰嗦了(其实我是不会哈哈哈哈哈。。。。。。。。)脑补吧。。。。。。。我知道各位都是大神



提醒:

        上述做法还需要用一个数组判断f[i][j]是否可以达到,或者你可以将f[i][j]的初值赋为-1,k不需要,因为k要求从i-1点击一次到i那么他的点击数一定大于0,故不需要




//本人是淳朴的C党#include <stdio.h>#include <stdlib.h>#include <math.h>#include <string.h>#define MAX(a,b) (a>b?a:b)#define MIN(a,b) (a>b?b:a)struct guan{    int x;    int up;    int down;}g[10010]={0};int change[10010][4]={0};int f[10010][1010]={0};int hash[10010][1010]={0};int t[10010][1010]={0};int k[10010][1010]={0};int n,m,e;void px(int l,int r){     int i=l,j=r;     struct guan t=g[l];     for(;i<j;)     {         for(;i<j;j--)             if(t.x>g[j].x)             {                 g[i]=g[j];                 break;             }         for(;i<j;i++)             if(t.x<g[i].x)             {                 g[j]=g[i];                 break;             }     }     g[i]=t;     if(i>l) px(l,i-1);     if(i<r) px(i+1,r);     return;}int main(){    int i,j,p,q;    int o=0;    freopen("bird.in","r",stdin);    freopen("bird.out","w",stdout);    scanf("%d%d%d",&n,&m,&e);    for(i=1;i<=n;i++)        scanf("%d%d",&change[i][1],&change[i][2]);    for(i=1;i<=e;i++)        scanf("%d%d%d",&g[i].x,&g[i].down,&g[i].up);    px(1,e);    for(i=1;i<=m;i++)    t[0][i]=1;    for(i=1,p=1;i<=n;i++)    {        o=0;        if(i>g[p].x)            p++;        if(i==g[p].x)        {        for(j=0;j<=g[p].down;j++)        hash[i][j]=1;    for(j=g[p].up;j<=m;j++)    hash[i][j]=1;}for(j=1;j<=m;j++){if(j-change[i][1]>=1){if(hash[i-1][j-change[i][1]]==0 && t[i-1][j-change[i][1]]==1){f[i][j]=f[i-1][j-change[i][1]]+1;t[i][j]=1;k[i][j]=f[i-1][j-change[i][1]]+1;if(hash[i][j]==0)o=1;}if(t[i][j-change[i][1]]==1 && k[i][j-change[i][1]]>0){if(t[i][j]==1){f[i][j]=MIN(f[i][j],k[i][j-change[i][1]]+1);k[i][j]=MIN(k[i][j],k[i][j-change[i][1]]+1);}else{f[i][j]=k[i][j-change[i][1]]+1;k[i][j]=k[i][j-change[i][1]]+1;t[i][j]=1;if(hash[i][j]==0)o=1;}}}if(j+change[i][2]<=m){if(hash[i-1][j+change[i][2]]==0 && t[i-1][j+change[i][2]]==1){if(t[i][j]==0){f[i][j]=f[i-1][j+change[i][2]];t[i][j]=1;if(hash[i][j]==0)o=1;}else f[i][j]=MIN(f[i][j],f[i-1][j+change[i][2]]);}}}for(j=m-change[i][1]+1;j<=m;j++){if(hash[i-1][j]==0 && t[i-1][j]==1){if(t[i][m]==0){f[i][m]=f[i-1][j]+1;t[i][m]=1;k[i][m]=f[i-1][j]+1;if(hash[i][m]==0)o=1;}else{f[i][m]=MIN(f[i][m],f[i-1][j]+1);k[i][m]=MIN(k[i][m],f[i-1][j]+1);}}if(t[i][j]==1 && k[i][j]>0){if(t[i][m]==1){f[i][m]=MIN(f[i][m],k[i][j]+1);k[i][m]=MIN(k[i][m],k[i][j]+1);}else{f[i][m]=f[i][j]+1;k[i][m]=k[i][j]+1;t[i][m]=1;if(hash[i][m]==0)o=1;}}}        if(o==0)        {            printf("0\n%d",p-1);            fclose(stdin);            close(stdout);            return 0;        }    }    j=2e9;    for(i=1;i<=m;i++)        if(t[n][i]==1)            j=MIN(j,f[n][i]);    printf("1\n%d",j);    fclose(stdin);    fclose(stdout);    return 0;}




0 0