Help Jimmy OpenJ_Bailian - 2978 (动态规划)

解析
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;
}

对于状态转移方程,可以用类似于下面的图来理解!在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值