问题描述
"Help Jimmy" 是在下图所示的场景上完成的游戏:
场景中包括多个长度和高度各不相同的平台。地面是最低的平台,高度为零,长度无限。
Jimmy 老鼠在时刻0 从高于所有平台的某处开始下落,它的下落速度始终为1 米/ 秒。
当Jimmy 落到某个平台上时,游戏者选择让它向左还是向右跑,它跑动的速度也是1 米/ 秒。
当Jimmy 跑到平台的边缘时,开始继续下落。Jimmy 每次下落的高度不能超过MAX米,不
然就会摔死,游戏也会结束。
设计一个程序,计算Jimmy 到地面时可能的最早时间。
输入数据
第一行是测试数据的组数t (0 <= t <= 20)。每组测试数据的第一行是四个整数 N,X,
Y,MAX,用空格分隔。N 是平台的数目(不包括地面),X 和Y 是Jimmy 开始下落的位置
的横竖坐标,MAX是一次下落的最大高度。接下来的 N 行每行描述一个平台,包括三个整
数,X1[i],X2[i]和H[i]。H[i]表示平台的高度,X1[i]和X2[i]表示平台左右端点的横坐标。1
<= N <= 1000 ,-20000 <= X, X1[i], X2[i] <= 20000 ,0 < H[i] < Y <= 20000(i = 1..N)。所有坐
标的单位都是米。
Jimmy 的大小和平台的厚度均忽略不计。如果Jimmy 恰好落在某个平台的边缘,被视
为落在平台上。所有的平台均不重叠或相连。测试数据保Jimmy 一定能安全到达地面。
输出要求
对输入的每组测试数据,输出一个整数,Jimmy到地面时可能的最早时间。
输入样例
1
3 8 17 20
0 10 8
0 10 13
4 14 3
输出样例
23
问题分析
这个问题的子问题是什么呢?首先,Jimmy从最初下落的地方到达板和每一次从板上落点走到板的左端或者右端的时间是很好计算的,我们只需要算出每一次下落时间即可,这样,子问题就是,每一次从左端或者右端作为起点到地面的最短时间,当我们把这些子问题求出来,从最开始的地方下落到地面的时间就可以通过每次不断的对下一次的判断选择来得出。该问题中的状态,就是板的编号,假设板总共有L个,我们对板从上到下依次编号k,同一高度的板编号次序无所谓,状态的值就是从该板落到地面所需的最短时间(从左端或者从右端)用mintime[]来存储。这里设如果一次下落的距离超过MAX,则时间mintime[]为INFINITE,则从某一块板k的左端下落到地面的最短时间可以这样算:
if(板k下方没有任何板子){
if(high[k] > MAX) leftmintime[k] = INFINITE; else leftmintime[k] = high[k];
}
else if(板k下方是板m){
if(high[k] - high[m] > MAX) mintime[k] = INFINITE;
else leftmintime[k] = min(leftmintime[m] + lx(k) - lx(m) , rightmintime[m] + rx(m) - lx(k) ) + high[k] - high[m];
}
其中,high[]是每块板到地面的高度,leftmintime[]指以板的左端作为起点开始下落所需的最短时间,rightimtime[]同理,lx()是板的左端横坐标,rx()是指板的右端横坐标。
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 #define INFINITE 100000 6 int leftmintime[1001]; 7 int rightmintime[1001]; 8 int N,X,Y,MAX; 9 struct Board 10 { 11 int lx; 12 int rx; 13 int high; 14 }board[1001]; 15 16 bool cmp(Board a,Board b) 17 { 18 if(a.high > b.high) return true; 19 else return false; 20 } 21 int mintime(int index,bool left) 22 { 23 int i,x; 24 int y = board[index].high; 25 if(left) 26 x = board[index].lx; 27 else 28 x = board[index].rx; 29 for(i = index + 1;i <= N;i++) 30 if(board[i].lx <= x && board[i].rx >= x) 31 break; 32 33 if(i <= N){ // 板index的下方有板 34 if(y - board[i].high > MAX) 35 return INFINITE; 36 } 37 else{ 38 if(y > MAX) 39 return INFINITE; 40 else 41 return y; 42 } 43 int lefttime = y - board[i].high + x - board[i].lx; 44 int righttime = y - board[i].high + board[i].rx - x; 45 if(leftmintime[i] == -1) 46 leftmintime[i] = mintime(i,true); 47 if(rightmintime[i] == -1) 48 rightmintime[i] = mintime(i,false); 49 lefttime+=leftmintime[i]; 50 righttime+=rightmintime[i]; 51 if(lefttime < righttime) 52 return lefttime; 53 else 54 return righttime; 55 } 56 57 int main() 58 { 59 int t; 60 61 cin >> t; 62 while(t--){ 63 int i; 64 memset(leftmintime,-1,sizeof(leftmintime)); 65 memset(rightmintime,-1,sizeof(rightmintime)); 66 cin >> N >> X >> Y >> MAX; 67 for(i = 1;i <= N;i++){ 68 cin >> board[i].lx >> board[i].rx >> board[i].high; 69 } 70 sort(board,board+N,cmp); //对板从上到下一次排序编号 71 72 board[0].lx = X; 73 board[0].rx = X; 74 board[0].high = Y; 75 cout << mintime(0,true); 76 } 77 return 0; 78 }