• 飞扬的小鸟


    洛咕

    题意:(Flappy) (Bird)是一款风靡一时的休闲手机游戏。玩家需要不断控制点击手机屏幕的频率来调节小鸟的飞行高度,让小鸟顺利通过画面右方的管道缝隙。如果小鸟一不小心撞到了水管或者掉在地上的话,便宣告失败。为了简化问题,我们对游戏规则进行了简化和改编:游戏界面是一个长为 (n),高为 (m) 的二维平面,其中有 (k) 个管道(忽略管道的宽度)。 小鸟始终在游戏界面内移动。小鸟从游戏界面最左边任意整数高度位置出发,到达游戏界面最右边时,游戏完成。小鸟每个单位时间沿横坐标方向右移的距离为 (1),竖直移动的距离由玩家控制。如果点击屏幕,小鸟就会上升一定高度 (X),每个单位时间可以点击多次,效果叠加;如果不点击屏幕,小鸟就会下降一定高度 (Y)。小鸟位于横坐标方向不同位置时,上升的高度 (X) 和下降的高度 (Y) 可能互不相同。小鸟高度等于 (0) 或者小鸟碰到管道时,游戏失败。小鸟高度为 (m) 时,无法再上升。现在,请你判断是否可以完成游戏。如果可以,输出最少点击屏幕数;否则,输出小鸟最多可以通过多少个管道缝隙.(5 leq n leq 10000), (5 leq m leq 1000)(0 leq k < n), (0 < X < m), (0 < Y < m), (0 < P < n), (0 leq L < H leq m), (L + 1 < H).

    分析:数据范围提示时间复杂度在(O(nm))左右??设(f[i][j])表示到达点((i,j))的最少点击屏幕数.则(f[i][j]=min(f[i][j-down[i-1]],f[i-1][j-k*up[i-1]]+k)),其中(down[i-1])表示在(i-1)处不点击屏幕下降的距离,(up[i-1])表示在(i-1)处点击屏幕上升的距离.这样的话时间复杂度是(O(nmk)),(k)大一点就会超时了.

    考虑把(k)优化掉,发现(k)的意思其实就是连着跳(k)次,而我们跳第(k)次显然可以直接从跳第(k-1)(O(1))转移过来.因为我们从小到大枚举纵坐标(j),所以更新(f[i][j])(f[i][j-up[i-1]])已经更新完了,我们直接从那里转移过来即可.

    然后还有一些细节问题放注释里面了.

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<map>
    #include<set>
    #define ll long long
    using namespace std;
    inline int read(){
        int x=0,o=1;char ch=getchar();
        while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
        if(ch=='-')o=-1,ch=getchar();
        while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
        return x*o;
    }
    const int N=10005;
    const int M=1005;
    int n,m,k,sum,ans=1e9,X[N],Y[N];
    int up[N],down[N],bj[N],f[N][M];
    inline bool check(int i){
    	for(int j=1;j<=m;++j)
    		if(f[i][j]!=1e9)return 1;
    	return 0;
    }
    int main(){
    	n=read();m=read();k=read();
    	for(int i=0;i<n;++i)up[i]=read(),down[i]=read();
    	for(int i=1;i<=k;++i){
    		int pos=read();bj[pos]=1;X[pos]=read();Y[pos]=read();
    	}
    	for(int i=1;i<=n;++i)
        	for(int j=0;j<=m;++j)f[i][j]=1e9;
    //i=0时任一位置都是0步,因为题目说了可以从最左边任意整数高度位置出发,其余初始化正无穷
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=m;++j){
    			if(j-up[i-1]>=1){
    				f[i][j]=min(f[i][j],f[i-1][j-up[i-1]]+1);//跳一次
    				f[i][j]=min(f[i][j],f[i][j-up[i-1]]+1);//连着跳
    			}
    		}
    		for(int j=m-up[i-1];j<=m;++j){//飞到顶部的时候要单独考虑
    			f[i][m]=min(f[i][m],f[i-1][j]+1);//跳一次
    			f[i][m]=min(f[i][m],f[i][j]+1);//连着跳
    		}
    		for(int j=1;j+down[i-1]<=m;++j)//不跳,直接下降过来
    			f[i][j]=min(f[i][j],f[i-1][j+down[i-1]]);
    //不能把不跳的情况与跳的情况一起更新,而要先更新跳再更新不跳,因为会出现以下这种情况:
    //(i,j)可以从(i-1,j+down[i-1)转移过来,但不能从(i-1,j-up[i-1])转移过来
    //如果我们放一起,那么更新连跳的时候的f[i][j-up[i-1]]可能是从上一次下降过来的
    //相当于你这一次做的动作是从(i-1,j+down[i-1])下降到(i,j),又从(i,j)上升到(i,j+up[i-1]),显然不合法
    		if(bj[i]){//如果这个地方是管子
    			for(int j=0;j<=X[i];++j)f[i][j]=1e9;
    			for(int j=Y[i];j<=m;++j)f[i][j]=1e9;//这些地方都不能走,还原为正无穷
    			int flag=0;
    			for(int j=X[i]+1;j<=Y[i]-1;++j)
    				if(f[i][j]!=1e9){flag=1;break;}//查找是否有能走的地方
    			if(flag)++sum;//sum记录通过的管子数量
    			else{printf("0
    %d
    ",sum);return 0;}
    		}
    		else if(!check(i)){printf("0
    %d
    ",sum);return 0;}
    	}
    	for(int j=1;j<=m;++j)ans=min(ans,f[n][j]);//找到最小的合法地方
    	printf("1
    %d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    思考:缓存的使用?灵活运用?
    线程和并发--基础
    java基础--方法
    java基础--数组
    数据结构复习笔记
    asp搭建网站
    转换磁盘格式
    cypress 与其他测试框架的区别
    cypress启动时报错file already exists,文件已经存在解决办法
    cypress的安装
  • 原文地址:https://www.cnblogs.com/PPXppx/p/11700540.html
Copyright © 2020-2023  润新知