沒刷過 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; }