P1941 飞扬的小鸟

    • 456通过
    • 2.4K提交
  • 题目提供者该用户不存在
  • 标签动态规划2014NOIp提高组
  • 难度提高+/省选-

提交该题 讨论 题解 记录

 

题目描述

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 。

前言:

考场上做的时候没思路,直接暴力dfs

50分代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
#include<map>
using namespace std;
#define ll long long
#define N 10010
#define inf 1100000000
#define linf 999999999999999LL
#define debug(x) cout<<"**"<<x<<endl
inline const int read(){
register int x=,f=;
register char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
inline const char in(){
for(register char ch=getchar();;ch=getchar()) if((ch>='A'&&ch<='Z')||(ch>='a'&&ch<='z')) return ch;
}
int n,m,k,pos;
int up[N],dn[N],xia[N],sha[N],ne[N],temp[N];
int g,exg,ans=0x7fffffff;
bool falg;
void dfs(int p,int nowh,int d){
if(p==n){
falg=;
ans=min(ans,d);
return ;
}
exg=max(exg,g);
int i,j,tmp;//bool tp=0;
if(xia[p+]||sha[p+]){
for(i=;i<=;i++){//上升
tmp=nowh+i*up[p];
if(tmp<xia[p+]) continue;
if(tmp<=sha[p+]){
//tp=1;
g++;
dfs(p+,tmp,d+i);
g--;
}
else break;
}
tmp=nowh-dn[p];//下降
if(tmp>=xia[p+]&&tmp<=sha[p+]){
g++;
dfs(p+,tmp,d);
g--;
}
return ;
}
for(i=;i<=;i++){//上升
tmp=nowh+i*up[p];
if(tmp<=m){
dfs(p+,tmp,d+i);
}
else{
dfs(p+,m,d+i);
break;
}
}
tmp=nowh-dn[p];//下降
if(tmp>){
dfs(p+,nowh-dn[p],d);
}
}
int main(){
//freopen("sh.txt","r",stdin);
n=read();m=read();k=read();
int i,j;
for(i=;i<n;i++) up[i]=read(),dn[i]=read();
for(i=;i<=k;i++) pos=read(),xia[pos]=read()+,sha[pos]=read()-;
/*for(j=1;j<=n;j++) if(xia[j]||sha[j]) break;
for(i=0;i<j;i++) ed+=dn[i];
if(ed>=xia[j]) ed=m;
else ed=xia[j]-ed;
for(i=0;i<j;i++) sd+=up[i];
if(sd>=xia[j]) sd=0;
else sd=xia[j]-sd;*/
for(i=;i<=m;i++) g=,dfs(,i,);
if(falg) printf("1\n%d",ans);
else printf("0\n%d",exg);
return ;
}

大家的说法:

50分 裸暴力

75分 暴力SPFA

转移方程:

f[i][j]表示到达(i,j)这个位置需要的最小点击次数
三个转移:
    向上点击不到顶: f[i][j]=min(f[i][j],f[i-1][j-x[i]]+1,f[i][j-x[i]]+1)
    向上点到顶:        f[i][M]=min(f[i][M],f[i-1][j]+1,f[i][j]+1)
这两个转移用到了完全背包一样的优化,就是点击多次的时候直接从上一次点击的转移。要注意的是需要用到的位置可能是不可到的(即上一次点击到的位置非法但多点一次就合法了),所以要每个位置都转移然后再把非法位置清空了(当然这个工作开一个辅助数组就行了 orz)。
    不点击向下掉:    f[i][j]=min(f[i][j],f[i-1][j+y[i]])

这里为了追寻先人精益求精的精神,这里用滚动数组写的代码

AC代码+题解:

#include<cstdio>
#include<algorithm>
using namespace std;
#define inf 1100000000
#define N 10010
inline const int read(){
register int x=,f=;
register char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
int n,m,k,pos,f[][N];//f[i][j]表示到达(i, j)这个位置需要的最小点击次数<这里滚动了第一维>
int up[N],dn[N],xia[N],sha[N];
//up[i]表示在横坐标i处能上升的高度
//dn[i]表示在横坐标i处能下降的高度
//sha[i]表示在横坐标i处(无论有无管道)能通过的最高高度为sha[i]-1
//xia[i]表示在横坐标i处(无论有无管道)能通过的最低高度为xia[i]+1
bool bo[N];
int main(){
n=read();m=read();k=read();
for(int i=;i<=n;i++) up[i]=read(),dn[i]=read();
for(int i=;i<=n;i++) sha[i]=m+;
for(int i=;i<=k;i++) pos=read(),xia[pos]=read(),sha[pos]=read(),bo[pos]=;
int last=,now=,guo=,ans=0x7fffffff;
for(int i=,j;i<=n;i++){
last=now;now^=;
for(j=;j<=m;j++) f[now][j]=inf;//初始化:i=1时,靠f[0][j]=0去更新
for(j=up[i]+;j<=m;j++) f[now][j]=min(f[now][j-up[i]],f[last][j-up[i]])+;//上升:从上一步的点击状态和点击状态转移过来(这样的话不会产生在一步之内既落下来又被点击的情况)
for(j=m-up[i];j<=m;j++) f[now][m]=min(f[now][m],min(f[now][j],f[last][j])+);//顶部m特判:所有在m-up[i]到m范围内的都可以转移到m
for(j=m-dn[i];j>=;j--) f[now][j]=min(f[now][j],f[last][j+dn[i]]);//下降:从本步高度可倒推出上一步高度,注意j的范围是本步之内的合理范围(不会碰到柱子),并且上一步高度不能高于m
for(j=;j<=xia[i];j++) f[now][j]=inf;//判断管道->判断能不能过去
for(j=sha[i];j<=m;j++) f[now][j]=inf;
for(j=;j<=m;j++) if(f[now][j]<inf){if(bo[i]) guo++;break;}//统计可以过去的管道数
if(j>m){printf("0\n%d\n",guo);return ;}
}
for(int i=;i<=m;i++) ans=min(ans,f[now][i]);
printf("1\n%d\n",ans);
return ;
}

AC 代码分析:

代码长度:1227B(去掉解释)

时空复杂度:

Codevs 3729==洛谷P1941 飞扬的小鸟的更多相关文章

  1. 洛谷 P1941 飞扬的小鸟

    洛谷 P1941 飞扬的小鸟 原题链接 首先吐槽几句 noip都快到了,我还不刷起联赛大水题! 题目描述 Flappy Bird 是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节 ...

  2. [洛谷P1941] 飞扬的小鸟

    洛谷题目链接:飞扬的小鸟 题目描述 Flappy Bird是一款风靡一时的休闲手机游戏.玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙.如果小鸟一不小心撞到了 ...

  3. [NOIP2014] 提高组 洛谷P1941 飞扬的小鸟

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

  4. 洛谷P1941飞扬的小鸟——细节DP

    题目:https://www.luogu.org/problemnew/show/P1941 此题主要注意许多细节,详见代码. 代码如下: #include<iostream> #incl ...

  5. 2018.11.06 洛谷P1941 飞扬的小鸟(背包)

    传送门 上升看成完全背包. 下降看成01背包. 注意边界转移就行了. 代码: #include<bits/stdc++.h> using namespace std; inline int ...

  6. 洛谷P1941 飞扬的小鸟 [noip2014] 背包

    正解:背包 解题报告: 话说好久没做背包的题了,都有些陌生了?这几天加强基础题目多刷点儿dp和背包趴qwq 其实这题是95...然后我下了我错的那个测试点,我答案是9874正解是9875...然后读入 ...

  7. 洛谷P1941 飞扬的小鸟(背包 dp)

    题意 题目链接 Sol 很显然的dp,设\(f[i][j]\)表示第\(i\)个位置,高度为\(j\)的最小步数 向上转移的时候是完全背包 向下转移判断一下就可以 #include<bits/s ...

  8. 洛谷 P1941 飞扬的小鸟【dp】

    设f[i][j]为在坐标(i,j)时的最小点击次数,转移的话从i-1向上飞或者向下掉转移,注意转移完了之后要把管子部分赋值为inf #include<iostream> #include& ...

  9. 洛谷P1941飞扬的小鸟 完全背包

    思维难度不大,就是有许多细节要注意. 1.不能开滚动数组. 2.要特判飞过天花板的情况. Code: #include<cstdio> #include<algorithm> ...

随机推荐

  1. jquery easyui的layout

    1.创建一个布局:layout是一个容器,它有5个区域:north(北),south(南),east(东),west(西):center(中)-----------学习中文网地址 <body c ...

  2. codeforces 651B Beautiful Paintings

    B. Beautiful Paintings time limit per test 1 second memory limit per test 256 megabytes input standa ...

  3. codeforces 630KIndivisibility(容斥原理)

    K. Indivisibility time limit per test 0.5 seconds memory limit per test 64 megabytes input standard ...

  4. [Xcode使用 - 3] 复制Xcode5.1.1中的项目模板到Xcode6.1

         由于Xcode6中精简了许多的项目和文件模板,导致开发非常不方便,所以这里简单介绍了怎么复制旧版本Xcode中的模板到新的Xcode中      这里要复制的是项目模板Empty Appli ...

  5. 转载C#中堆(heap)和栈(stack)的区别

    转载原地址  http://www.cnblogs.com/wangshenhe/archive/2013/02/18/2916275.html [转]C#堆和栈的区别 理解堆与栈对于理解.NET中的 ...

  6. [置顶] 函数传递不定参数理解-c语言

    感性认识 Typedef char *va_list;/*这个在<stdatg.h>中有定义*/ #define va_start(ap,p) (ap=(char*)(&(p)+1 ...

  7. HTML第一天学习笔记

  8. 栈的应用2——超级计算器(中缀与后缀表达式)C语言

    输入中缀表达式输出结果(结果可以是小数,但输入必须是整数)  #include<stdio.h> #include<stdlib.h> //需要两个栈,一个储存结果,一个储存运 ...

  9. C# WinForm控件、自定义控件整理(大全)

    转:http://www.cnblogs.com/top5/archive/2010/04/29/1724039.html 对C# WinForm开发系列收集的控件使用方面进行整理, 加入了一些文章, ...

  10. MDF文件数据恢复