zoukankan      html  css  js  c++  java
  • POJ 1661 Help Jimmy(C)动态规划

    没刷过 POJ,这题是论坛有人问的,我才看看。
    我发现 POJ 注册很奇怪,账号总是登不上去,弄的我还注册两个。Emmm 首次体验很差,还好我不在 POJ 刷题。

    题目链接:POJ 1661 Help Jimmy


    解题思路

    我最初想的是用递归从上往下不断选择方向,结果发现我有点傻了,这样极有可能 TLE。

    其实应该是用动态规划解题。从下往上,将每块平台的左端点和右端点到地面的最短时间计算出来。最后得到人的最短时间。

    思路详解:

    很明显,平台有三个数据,左端点,右端点,高度。因此直接定义结构体如下:

    typedef struct Node {
    	int left, right, height;
    }Platform;// 平台
    Platform plat[MAX_N];// 所有平台信息
    

    这里有个细节就是将地面都看做平台。地面和人的数据如下:

    // 地面,地面高度最小,故保存在数组第一个
    plat[0].left = -20000;
    plat[0].right = 20000;
    plat[0].height = 0;
    // 人,人高度最大,故保存在数组最后一个
    plat[platNum - 1].left = plat[platNum - 1].right = x;
    plat[platNum - 1].height = y;
    

    升序排序。一如既往的使用 qsort 函数,自定义比较函数 compare

    从下往上遍历每个平台,分别计算左右端点的最短时间。保存在二维数组 time[MAX_N][2] 中。

    计算数组的方法(以 time[i][0] 为例,ij 表示平台):

    遍历 i 下方的平台,找到一个人不会摔死的平台 j。(如果会摔死,说明 i 的端点 dir 是悬崖,退出 down 函数)

    如果找到 j 位于 i 的下方。判断 j 是不是地面。

    如果是地面,那么 i 的高度就是 i 的端点 dir 的最短时间。time[i][dir] = plat[i].height

    如果不是地面,那么 j 是平台(不包括地面),如下图所示:
    在这里插入图片描述
    补充:图中的 h 改为 th

    需要注意的是,time[j][0]time[j][1] 本身可能就是悬崖。具体看代码的 if 条件判断。这种情况有其他处理方法,暂且不表。

    如果都不是悬崖,那么状态转移方程为:time[i][dir] = MIN(time[j][0] + tl, time[j][1] + tr) + th;

    thtltr 这三个量很容易计算。不做说明。

    因此最后的 time[i][0],其中 i 是人的下标,即为最终结果。

    时间复杂度:main 函数里面有一个 for 循环,down() 里面有 for 循环。故为 O(n2)O(n^2)
    空间复杂度:结构体数组加上一个二维数组,总共不低于 O(n)O(n)

    C代码

    #include<stdio.h>
    #include<stdlib.h>
    #include<limits.h>// INT_MAX头文件
    
    #define MAX_N 1003
    #define MIN(a, b) (((a) < (b)) ? (a) : (b))
    
    typedef struct Node {
    	int left, right, height;
    }Platform;// 平台
    Platform plat[MAX_N];// 所有平台信息
    int platNum;// 平台个数
    
    int time[MAX_N][2];// 平台左端点和右端点到地面的最短时间
    
    int maxHeight;// 每次下落的最大高度
    
    int compare(const Platform* a, const Platform* b) {// 比较函数,平台高度升序
    	return (*a).height - (*b).height;
    }
    
    // 下落
    // i	当前平台
    // dir	下落后选择的方向,0表示左,1表示右
    // x	平台i的端点横坐标,必须与dir对应
    void down(int i, int dir, int x) {
    	// 查找i正下方的j
    	int j;
    	for (j = i - 1; j >= 0; --j) {
    		if (plat[i].height - plat[j].height > maxHeight) {// i和j的高度差超过最大值
    			time[i][dir] = INT_MAX;// 人会摔死,因此时间为正无穷,表示悬崖
    			return;
    		}
    		if (x >= plat[j].left && x <= plat[j].right) {// j在i正下方
    			break;// 已找到,退出循环
    		}
    	}
    
    	if (j == 0) {// j是地面
    		time[i][dir] = plat[i].height;// i的高度就是i的端点dir最短时间
    		return;
    	}
    
    	// j是平台(不包括地面),计算i的端点dir到地面的最短时间
    	int tl = x - plat[j].left;// i的端点到j的左端点的水平时间
    	int tr = plat[j].right - x;// i的端点到j的右端点的水平时间
    	int th = plat[i].height - plat[j].height;// i到j的垂直下落时间
    	if (time[j][0] == INT_MAX) {// j左边是悬崖
    		if (time[j][1] == INT_MAX) {// j右边是悬崖
    			time[i][dir] = INT_MAX;// 那么i的端点dir也是悬崖
    		} else {
    			time[i][dir] = time[j][1] + tr + th;// 走j的右边
    		}
    	} else {
    		if (time[j][1] == INT_MAX) {
    			time[i][dir] = time[j][0] + tl + th;// 走j的左边
    		} else {// j的左边和右边都不是悬崖,选择j的时间短的方向
    			time[i][dir] = MIN(time[j][0] + tl, time[j][1] + tr) + th;
    		}
    	}
    }
    
    int main() {
    	int t, n, x, y, max;
    	
    	scanf("%d", &t);// 样例数
    	while (t--) {
    		scanf("%d %d %d %d", &n, &x, &y, &max);
    
    		maxHeight = max;// 每次下落的最大高度
    
    		platNum = n + 2;// 平台,人,地面。共n+2个“平台”
    		
    		// 地面,地面高度最小,故保存在数组第一个
    		plat[0].left = -20000;
    		plat[0].right = 20000;
    		plat[0].height = 0;
    		// 人,人高度最大,故保存在数组最后一个
    		plat[platNum - 1].left = plat[platNum - 1].right = x;
    		plat[platNum - 1].height = y;
    
    		// 输入所有平台的左右端点坐标和高度
    		for (int i = 1; i < platNum - 1; ++i) {
    			scanf("%d %d %d", &plat[i].left, &plat[i].right, &plat[i].height);
    		}
    
    		qsort(plat, platNum, sizeof(Platform), compare);// 平台按照高度升序
    
    		time[0][0] = time[0][1] = 0;// 地面时间为0
    		
    		for (int i = 1, j; i < platNum; ++i) {// 从下往上计算每个平台的最短用时
    			down(i, 0, plat[i].left);// 从i下落后走左边
    			down(i, 1, plat[i].right);// 从i下落后走右边
    		}
    
    		printf("%d
    ", time[platNum - 1][0]);// 输出人到地面的最短用时
    	}
    
    	return 0;
    }
    

    提交结果

    在这里插入图片描述

  • 相关阅读:
    ffplay 一些好玩的filter
    ffmpeg加文字水印并控制水印显示时间或显示周期
    学习笔记之redux
    vue的一些常识代码规范(小小总结)
    使用computed和watch实现子组件监听父组件的变量变化
    vuex实现状态管理的具体操作
    scss使用总结
    vue的key值引发渲染错位的血案
    mp-vue实现小程序回顶操作踩坑,wx.pageScrollTo使用无效填坑
    git 的一些指令 (遇到再补充)
  • 原文地址:https://www.cnblogs.com/wowpH/p/11687397.html
Copyright © 2011-2022 走看看