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