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,故不需要
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; }
相关文章推荐
- NOIP2014复赛提高组day1(A:生活大爆炸版石头剪刀布 B:联合权值 C:飞扬的小鸟)
- [NOIP2014] 提高组 洛谷P2038 无线网络发射器选址
- NOIP2014提高组 DAY1 -SilverN
- noip2014提高组day2二题题解-rLq
- NOIP 2014 提高组 Day1 T2 联合权值
- Noip 2014 提高组 联合权值
- NOIP 2014 提高组 无线网路发射器选址
- NOIP2014复赛提高组day2(A:无线网络发射器选址 B:寻找道路 C:解方程)
- NOIP 2014 提高组 Day1
- 【模拟】洛谷 P1328 NOIP2014提高组 day1 T1 生活大爆炸版石头剪刀布
- 无线网络发射选址 NOIP2014 提高组 Day2 T1
- 洛谷-生活大爆炸版石头剪刀布-NOIP2014提高组复赛
- [NOIP2014] 提高组 洛谷P2296 寻找道路
- NOIP 2014 提高组 Day2 T2 寻找道路
- 2014 Noip提高组 Day1
- 【前缀和】【前缀MAX】洛谷 P1351 NOIP2014提高组 day1 T2 联合权值
- 解方程 NOIP2014 提高组 Day2 T3
- NOIp2014提高组 寻找道路->蜜汁最短路
- [NOIP2014] 提高组 洛谷P2312 解方程
- NOIP 2014 提高组 Day2 T1 无线网路发射器选址