【三维DP】POJ 3361 Running

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

这是一道2018年北大叉院大数据中心的一个题目,因为最近在准备夏令营,所以刷一下。前三题秒过,这题就卡了一些时间,感觉我差不多就四题的水平了。题目的大致意思,就是一个奶牛要跑步,一共要跑N分钟,其疲劳度不能超过M,每分钟可以跑的距离由一个长度为N的数组D给出,并且要求跑步途中疲劳度小于等于M,每分钟如果跑步,疲劳度增加1,如果休息,疲劳度减少1,需要注意的是,如果你选择休息,就需要将疲劳度休息为0之后再跑。最终要求跑完之后,疲劳度恢复0.求奶牛所能跑的最远距离。

具体的中文翻译可以去看百炼oj上的题目。

二、题目思路以及AC代码

这题一开始看到,由于前面三题的难度迷惑性,我以为是贪心题,因为传统的那个沙漠加油题,也是这个样子的,最终要求油量剩余为0,所以我就朝着从结尾贪心的方向琢磨,发现限制太多,好像不行。

然后换思路,不知道怎么就想到了DP,然后一开始定义二维dp[i][j]的含义是前i分钟,疲劳度剩余j所能跑的最远距离,发现有一个要求满足不了,就是他要求只要选择休息,就要把疲劳度恢复为0再开始跑步。那么我就想到可以多存一个bool值,用于定义之前是否是在休息,一开始采用pair存储,发现还是无法表示,最后才考虑到使用三维DP。(我是真的没想到,之前第三题还是一个最基本的bfs,然后到第四题就变成了三维DP,好在最后想到了)

有了之前的思考之后,我们定义dp[i][j][k],表示前i分钟,奶牛疲劳度为j,状态为k的能跑的最大距离。其中状态k取值为0,1,0表示正在跑步,1表示正在休息。

这样的话,我们就可以构造基本的状态转移方程:
在这里插入图片描述
在这里插入图片描述
相信把这个式子看懂应该不是很难,我大致的解释一下,第一个式子是表示如何求解i分钟疲劳度为j,并且当时在休息的最大跑步距离,它可以由如下两个状态转移过来,一个是在i-1分钟,疲劳度为j+1,当时在休息,一个是在i-1分钟,疲劳度为j-1,当前在跑步。下面的式子类似理解。

可能会有细心的同学注意到,这个式子并不是完全正确的,因为会有一些特殊的情况,比如我原本就一直在休息,疲劳度是0,然后我可以转移到的状态就是下一分钟我还在休息,疲劳度还是0,那么这种情况如果表示的话,就是在j==0的情况下,dp[i][j][1] = max(dp[i-1][j][0], dp[i-1][j+1][0], dp[i-1][j+1][1]),其余还有几种特殊情况可以参见我的代码。

这里需要注意几点,一个是我上面提到的一些特殊情况,还有一个是需要定义好非法状态,如果其中上述式子右边两个都是非法状态,那么结果也就是非法状态,而不能存有数值。

上边就是这道题的一些重点,这里也没有办法细讲,毕竟讲起来也挺费劲的,也不一定说的明白,不明白的同学就照着代码好好看看吧!(其实我觉得自己可能做的复杂了, 500ms)

下面给出AC代码:

#include <iostream>
#include <algorithm>
#define MAXN 10010
#define MAXM 505
using namespace std;

int dp[MAXN][MAXM][2];

int d[MAXN];
int n, m;

int value(int i, int j, int k) {
	if (i < 0 || i > n || j < 0 || j > m || k < 0 || k > 1) {
		return -1;
	}
	return dp[i][j][k];
}

int main()
{
	cin >> n >> m;

	for (int i = 1; i <= n; i++) {
		cin >> d[i];
	}

	for (int i = 0; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			for (int k = 0; k <= 1; k++) {
				dp[i][j][k] = -1;
			}
		}
	}

	dp[0][0][1] = 0;

	for (int i = 1; i <= n; i++) {
		for (int j = 0; j <= m; j++) {
			if (j == 0) {
				if (value(i - 1, 0, 1) != -1) {
					dp[i][j][1] = max(value(i - 1, j + 1, 0), value(i - 1, j + 1, 1));
					dp[i][j][1] = max(dp[i][j][1], value(i - 1, 0, 1));
				}
				else {
					dp[i][j][1] = max(value(i - 1, j + 1, 0), value(i - 1, j + 1, 1));
				}
			}
			else {
				if (value(i - 1, j + 1, 0) == -1 && value(i - 1, j + 1, 1) == -1)
					dp[i][j][1] = -1;
				else
					dp[i][j][1] = max(value(i - 1, j + 1, 0), value(i - 1, j + 1, 1));
			}
			
			if (j == 1) {
				if (value(i - 1, j - 1, 0) == -1 && value(i - 1, j - 1, 1) == -1)
					dp[i][j][0] = -1;
				else
					dp[i][j][0] = max(value(i - 1, j - 1, 0) + d[i], value(i - 1, j - 1, 1) + d[i]);
			}
			else
				if (value(i - 1, j - 1, 0) != -1)
					dp[i][j][0] = value(i - 1, j - 1, 0) + d[i];
		}
	}

	cout << dp[n][0][1] << endl;

    return 0;
}

如果有问题,欢迎大家留言!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值