没刷过 POJ,这题是论坛有人问的,我才看看。
我发现 POJ 注册很奇怪,帐号老是登不上去,弄的我还注册两个。Emmm 首次体验不好,还好我不在 POJ 刷题。html
题目连接:POJ 1661 Help Jimmyweb
我最初想的是用递归从上往下不断选择方向,结果发现我有点傻了,这样极有可能 TLE。数组
其实应该是用动态规划解题。从下往上,将每块平台的左端点和右端点到地面的最短期计算出来。最后获得人的最短期。app
思路详解:svg
很明显,平台有三个数据,左端点,右端点,高度。所以直接定义结构体以下:函数
typedef struct Node { int left, right, height; }Platform;// 平台 Platform plat[MAX_N];// 全部平台信息
这里有个细节就是将地面和人都看作平台。地面和人的数据以下:spa
// 地面,地面高度最小,故保存在数组第一个 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
。code
从下往上遍历每一个平台,分别计算左右端点的最短期。保存在二维数组 time[MAX_N][2]
中。orm
计算数组的方法(以 time[i][0]
为例,i
,j
表示平台):xml
遍历 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;
th
,tl
,tr
这三个量很容易计算。不作说明。
所以最后的 time[i][0]
,其中 i
是人的下标,即为最终结果。
时间复杂度:main
函数里面有一个 for
循环,down()
里面有 for
循环。故为
。
空间复杂度:结构体数组加上一个二维数组,总共不低于
。
#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\n", time[platNum - 1][0]);// 输出人到地面的最短用时 } return 0; }