题目链接:
[UVA 1632]Alibaba[区间DP]
题意分析:
有n个宝藏,每个都在一个位置p[i],在规定的时间t[i]就会消失。阿里巴巴要在宝藏消失前收集齐所有宝藏,问:有方法吗?有的话最少多少秒?
解题思路:
嘛,取宝藏肯定是一个连续的区间都取掉,故意不拿绝对不是最优策略。所以整个策略就包括两种:向左走还是向右走。设状态为dp[l][r][2]。0代表阿里巴巴在左端点,1代表阿里巴巴在右端点。整个含义是:取到这一区间所需要的最少时间。下面来分析下转移,单独分析左端点(右端点类似),dp[l][r][0] = min(dp[l + 1][r][0] + p[l + 1] - p[l], dp[l + 1][r][1] + p[r] - p[l]);要么是由上个区间左边走过来的,要么是由上个区间右边走过来的。
正是由于dp[l][r][i]代表的是到达这个状态的时间,所以再更新了之后,比较一下更新之后的最小时间和规定时间,如果超过了,那么久赋值为INF。
由于更新时使用到了小区间的值,所以这题更新的时候我们从小区间往大区间更新。整个循环的顺序也就确定下来了。
个人感受:
第二次做这题了。依稀只记得状态是三维。想半天,卡在怎么表示时间上,心里想着我明明是要用dp求时间,却又要拿时间来考虑是否能拿到宝藏,觉得甚是矛盾,哎~重做的好处体现出来了。这次又加深了理解。
具体代码如下:
#include<iostream>
#include<cstdio>
using namespace std;
const int INF = 0x7f7f7f7f, MAXN = 1e4 + 111;
int p[MAXN], t[MAXN], n, dp[MAXN][MAXN][2];
int main()
{
while(~scanf("%d", &n))
{
for (int i = 1; i <= n; ++i)
scanf("%d%d", &p[i], &t[i]);
int ans, cur;
for (int i = n; i >= 1; --i)
for (int j = i + 1; j <= n; ++j)
{
dp[i][j][0] = min(dp[i + 1][j][0] + p[i + 1] - p[i],
dp[i + 1][j][1] + p[j] - p[i]);
if (dp[i][j][0] >= t[i]) dp[i][j][0] = INF;
dp[i][j][1] = min(dp[i][j - 1][0] + p[j] - p[i],
dp[i][j - 1][1] + p[j] - p[j - 1]);
if (dp[i][j][1] >= t[j]) dp[i][j][1] = INF;
}
ans = min(dp[1][n][0], dp[1][n][1]);
if (ans == INF) cout << "No solution\n";
else cout << ans << '\n';
}
return 0;
}
博客介绍了如何使用动态规划解决UVA 1632问题,即在宝藏消失前收集所有宝藏的最短时间。解题思路强调了连续区间选择的重要性,并详细说明了状态dp[l][r][2]的含义和状态转移方程。博主分享了个人重做此题的心得体会,加深了对动态规划的理解。

186

被折叠的 条评论
为什么被折叠?



