解析
Jimmy 要下到下一块板,不是在当前板子的左侧下去,就是在当前板子的右侧下去。所以要到达每块板子最左侧和最右侧的时间。
这题可以从上往下找到达板子边缘最小时间 或 从下往上找板子边缘的最小时间。
从上往下找到达板子边缘最小时间:用了递归
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
const int INF = 1 << 30;
const double EPS = 1e-6;
#define MX 52
//#define Mod 998244353
using namespace std;
struct brank
{
int Left, Right, H;
bool operator <(const brank& b) const{
return H > b.H;
}
};
brank A[1005];
int N, X, Y, MAX, LeftMinTime[1005], RightMinTime[1005];
int LeftMINTime(int);
int RightMINTime(int);
int LeftMINTime(int k)
{
if (LeftMinTime[k] != 0 || k == N + 1) return LeftMinTime[k];
bool f(0);
int m;
F(i, k + 1, N){//找板子k左端正下方有没别的板子
if (A[i].H < A[k].H && A[i].Left <= A[k].Left &&A[i].Right >= A[k].Left && A[k].H - A[i].H <= MAX){
f = 1;
m = i;
//printf("Lm=%d\n", m);
break;
}
}//F(i, k + 1, N){//找板子k左端正下方有没别的板子
if (f == 0){
if (A[k].H > MAX) LeftMinTime[k] = INF;
else LeftMinTime[k] = A[k].H;
}
else //板子k左边下端的板子为m
LeftMinTime[k] = A[k].H - A[m].H + min(LeftMINTime(m) + A[k].Left - A[m].Left, RightMINTime(m) + A[m].Right - A[k].Left);
return LeftMinTime[k];
}
int RightMINTime(int k)
{
if (RightMinTime[k] != 0 || k == N + 1) return RightMinTime[k];
bool f(0);
int m;
F(i, k + 1, N){//找板子k右端正下方有没别的板子
if (A[i].H < A[k].H && A[i].Right >= A[k].Right && A[i].Left <= A[k].Right && A[k].H - A[i].H <= MAX){
f = 1;
m = i;
//printf("Rm=%d\n", m);
break;
}
}//F(i, k + 1, N){//找板子k左端正下方有没别的板子
if (f == 0){
if (A[k].H > MAX) RightMinTime[k] = INF;
else RightMinTime[k] = A[k].H;
}
else //板子k右边下端的板子为m
RightMinTime[k] = A[k].H - A[m].H + min(LeftMINTime(m) + A[k].Right - A[m].Left, RightMINTime(m) + A[m].Right - A[k].Right);
return RightMinTime[k];
}
int main()
{
IOS;
//FO;
int T;
cin >> T;
while (T--){
memset(RightMinTime, 0, sizeof(RightMinTime));
memset(LeftMinTime, 0, sizeof(LeftMinTime));
cin >> N >> X >> Y >> MAX;
N++;
A[1].Left = X, A[1].Right = X, A[1].H = Y;
F(i, 2, N) cin >> A[i].Left >> A[i].Right >> A[i].H;
sort(A + 1, A + N + 1);//以板子高度进行排序
A[N + 1].H = 0, A[N + 1].Left = -INF, A[N + 1].Right = INF;//边界条件
//F(i, 1, N) dBug(A[i].H);
cout << RightMINTime(1) << endl;//note:要从起点开始
//F(i, 1, N) dBug(LeftMinTime[i]); ed;
//F(i, 1, N) dBug(RightMinTime[i]); ed;
}
return 0;
}
LeftMinTime[i]表示到达第i块平面的左边的最少时间。
状态转移方程为:
LeftMinTime[k] = A[k].H - A[m].H + min(LeftMINTime(m) + A[k].Left - A[m].Left, RightMINTime(m) + A[m].Right - A[k].Left);
右边同理。
从下往上找板子边缘的最小时间:先把状态转移方程需要用到的值求出了
#pragma GCC optimize(3,"Ofast","inline")
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<vector>
#include<cstring>
#include<queue>
#include<stack>
#include<list>
#include<map>
#include<set>
#include<cmath>
#include<sstream>
#include<cstdlib>
#include<bitset>
#define F(i,s,t) for(int i=(s);i<=(t);i++)
#define D(i,s,t) for(int i=(s);i>=(t);i--)
#define dBug(i) printf("Value=%d\n",i)
#define ddBug(i,j) printf("Value=%d %d\n",i,j)
#define ed putchar('\n')
#define FO freopen("D:\\in.txt","r",stdin)
#define IOS cin.tie(0) ,cout.tie(0), cout.sync_with_stdio(0)
typedef long long ll;
const int INF = 1 << 30;
//const double EPS = 1e-6;
//#define MX 102
//#define Mod 10000
using namespace std;
struct brank
{
int Left, Right, H;
bool operator<(const brank& b)const{
return H>b.H;//从大到小排序
}
};
brank A[1005];
int N, X, Y, MAX, LeftMinTime[1005], RightMinTime[1005];//LeftMinTime[i]表示到达第i块板的最左边时的最小时间
void left(int);
void right(int);
void left(int k)
{
int i = k + 1;//因为从大到小排序,所以i实际比k低了
while (i <= N && A[k].H - A[i].H <= MAX){
if (A[k].Left >= A[i].Left && A[k].Left <= A[i].Right){
LeftMinTime[k] = A[k].H - A[i].H + min(LeftMinTime[i] + A[k].Left - A[i].Left,
RightMinTime[i] + A[i].Right - A[k].Left);//动态转移方程
return;
}//if
i++;//看更低的一块板
}//while
if (A[k].H - A[i].H > MAX) LeftMinTime[k] = INF;
else LeftMinTime[k] = A[k].H;
}
void right(int k)//k表示当前层数
{
int i = k + 1;//因为从大到小排序,所以i实际比k低了
while (i <= N && A[k].H - A[i].H <= MAX){//保证下一块板i存在且可达
if (A[k].Right >= A[i].Left && A[k].Right <= A[i].Right){
RightMinTime[k] = A[k].H - A[i].H +
min(LeftMinTime[i] + A[k].Right - A[i].Left, RightMinTime[i] + A[i].Right - A[k].Right);//状态转移方程
return;
}
i++;//看更低的一块板
}//while
if (A[k].H - A[i].H > MAX) RightMinTime[k] = INF;//不能到达下一平台
else RightMinTime[k] = A[k].H;//直接落地l
}
int main()
{
IOS;
//FO;
int T;
cin >> T;
while (T--){
memset(RightMinTime, 0, sizeof(RightMinTime));
memset(LeftMinTime, 0, sizeof(LeftMinTime));
cin >> N >> X >> Y >> MAX;
//N++;
A[0].Left = X, A[0].Right = X, A[0].H = Y;//起点,看作高度最高
F(i, 1, N) cin >> A[i].Left >> A[i].Right >> A[i].H;
sort(A + 1, A + N + 1);//1到N排序,node:左开右闭
A[N + 1].H = 0, A[N + 1].Left = -INF, A[N + 1].Right = INF;
for (int i = N; i >= 0; i--){//先看低的板,因为是降序嘛
left(i);//该函数相当于给LeftMinTime[i]赋值
right(i);//由上面板子的最优解推出RightMinTime[i]
}
cout << RightMinTime[0] << endl;//实际上RightMinTime[0]与LeftMinTime[0] 相同
}//while
return 0;
}
对于状态转移方程,可以用类似于下面的图来理解!