您的位置:首页 > 其它

noip2014 提高组题解 bird

2014-11-15 14:00 169 查看
题目描述:

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
[i]==1)
            j=MIN(j,f
[i]);
    printf("1\n%d",j);
    fclose(stdin);
    fclose(stdout);
    return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: