POJ 3042 区间DP,这道题有点技巧。如果把状态转移方程定义为dp[i][j]:=吃光从i-j的草,i-j的草的新鲜度之和,是求不出来状态转移表达式的。原因很简单,因为忽视了吃光所有的草的时候牛站在区间哪一侧的重要信息。
一个比较好的定义是这样的:dp[i][j][1]:=吃光i-j的草的时候,牛站在j位置,此时此刻全部的草的新鲜度之和,0则代表站在i位置。
这样可以很容易的求出状态转移方程:
dp[i][j][1] = min(dp[i][j - 1][1] + (arr[j] - arr[j - 1]) * (N - (j - i)),
dp[i][j - 1][0] + (arr[j] - arr[i]) * (N - (j - i)))
以上方程讨论了吃到j的时候,是从i位置过来的好还是从j-1位置过来的,(arr[j] - arr[j - 1]) * (N - (j - i))的意义是:从j-1的位置走到j还有N-(j-i)个草没有被吃,他们的新鲜度全部加上arr[j] - arr[j - 1]。
此题可见,状态方程的定义,是十分重要的。
在状态转移方程定义时将区间端点的左右也建立成状态,最近遇到了3次,对于区间左右的01表示是一个值得学习的技巧。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int N, L;
int arr[1001];
int dp[1001][1001][2];
int main(){
scanf("%d %d", &N, &L);
for(int i = 1; i <= N; i ++){
scanf("%d", arr + i);
}
sort(arr + 1, arr + 1 + N);
for(int i = 1; i <= N; i ++){
dp[i][i][0] = dp[i][i][1] = N * abs(L - arr[i]);
}
for(int l = 2; l <= N; l ++){
for(int i = 1, j = i + l - 1; j <= N; j ++, i ++){
dp[i][j][1] = min(dp[i][j - 1][1] + (N - j + i) * (arr[j] - arr[j - 1]), dp[i][j - 1][0] + (N - j + i) * (arr[j] - arr[i]));
dp[i][j][0] = min(dp[i + 1][j][1] + (N - j + i) * (arr[j] - arr[i]), dp[i + 1][j][0] + (N - j + i) * (arr[i + 1] - arr[i]));
}
}
printf("%d\n", min(dp[1][N][1], dp[1][N][0]));
}