POJ 1661 Help Jimmy(C)動態規劃

沒刷過 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 函數,自定義比較函數 comparecode

從下往上遍歷每一個平臺,分別計算左右端點的最短期。保存在二維數組 time[MAX_N][2] 中。orm

計算數組的方法(以 time[i][0] 爲例,ij 表示平臺):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;

thtltr 這三個量很容易計算。不作說明。

所以最後的 time[i][0],其中 i 是人的下標,即爲最終結果。

時間複雜度:main 函數裏面有一個 for 循環,down() 裏面有 for 循環。故爲 O ( n 2 ) 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\n", time[platNum - 1][0]);// 輸出人到地面的最短用時
	}

	return 0;
}

提交結果

在這裏插入圖片描述

相關文章
相關標籤/搜索