[UVA 1632]Alibaba[区间DP]

题目链接: [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;
}


你可能感兴趣的:(dp,uva)