bzoj3379

区间dp

好神

看上去没有思路,因为觉得完成没有顺序,没有明显的转移顺序,转移的时候没办法记录之前已经完成哪些,那么转移就不能保证任务全部完成。但是我们发现其实没完成的任务一定是一段连续的区间,那么我们就可以进行区间dp,dp[i][j][0/1]表示当前未完成的区间是[i,j],现在正在完成i或j,0是i,1是j,花费的最小时间,最终答案就是dp[i][i][0/1]+dis(i,B),转移比较简单,0和1分别两种情况讨论

以前某道树形dp也是觉得选择的东西不一定连续,很难dp,最终证明选择的肯定是连续的一段,然后就可以进行区间dp,所以当我们看见这种由于选择方式奇怪而不能直接像背包一样dp的,或者是可以不连续选择而需要dp的题目,可以考虑证明选择的东西一定是连续的一段,然后进行区间dp或者其他dp

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
struct data {
    int pos, t;
    bool friend operator < (const data &a, const data &b) {
        return a.pos < b.pos;
    }
} a[N];
int n, len, end;
int dp[N][N][2];
int main()
{
    scanf("%d%d%d", &n, &len, &end);
    for(int i = 1; i <= n; ++i) scanf("%d%d", &a[i].pos, &a[i].t);
    sort(a + 1, a + n + 1);
    memset(dp, 0x3f3f, sizeof(dp));
    dp[1][n][0] = max(a[1].pos, a[1].t);
    dp[1][n][1] = max(a[n].pos, a[n].t);
    for(int i = 1; i <= n; ++i)
        for(int j = n; j >= i; --j)
        {
            dp[i][j][0] = min(dp[i][j][0], max(dp[i - 1][j][0] + a[i].pos - a[i - 1].pos, a[i].t));
            dp[i][j][0] = min(dp[i][j][0], max(dp[i][j + 1][1] + a[j + 1].pos - a[i].pos, a[i].t));
            dp[i][j][1] = min(dp[i][j][1], max(dp[i - 1][j][0] + a[j].pos - a[i - 1].pos, a[j].t));
            dp[i][j][1] = min(dp[i][j][1], max(dp[i][j + 1][1] + a[j + 1].pos - a[j].pos, a[j].t));
        }
    int ans = 0x3f3f3f3f;
    for(int i = 1; i <= n; ++i) ans = min(ans, min(dp[i][i][0], dp[i][i][1]) + abs(a[i].pos - end));
    printf("%d\n", ans);    
    return 0;
} 
View Code

 

posted @ 2017-10-18 20:27  19992147  阅读(224)  评论(0编辑  收藏  举报