题目链接(luogu)
根据题目条件和数据范围不难看出这是一道区间dp。
定义子状态$dp[i][j]$代表吃区间$[i,j]$内的pie可以得到的最大价值。
如果不增加新的奶牛可以得到转移方程:$dp[i][j]=max(dp[i][j], dp[i][k]+dp[k+1][j])$。
如果要新增一个奶牛,则必定有一个k是要被吃的,可以得到转移方程:$dp[i][j]=max(dp[i][j], dp[i][k-1]+dp[k+1][j]+f[k][i][j])$。
其中$f[k][i][j]$代表满足$i le l le k le r le j$的奶牛的最大价值。
Q:为什么要求$l,r$在$i,j$内呢?
A:因为dp的无后效性,所选的牛不能影响后面,可以理解为目前的定义域只有$[i,j]$。
Q:怎么求出$f$呢?
A:同样考虑区间dp,我们发现对于每个k的f是不相干的,可以分开求。
具体来讲,$f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j])$,$f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j])$。
有初值:$f[k][l][r]=w[i]$,不用取max因为每个牛的$l,r$一定不同(题目要求)。
Code:
for (int k = 1; k <= n; k++) { for (int i = k; i >= 1; i--) { for (int j = k; j <= n; j++) { if (i != 1) f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j]); if (j != n) f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j]); } } }
之后的转移也就非常简单了,初值$dp[i][i]=f[i][i][i]$。
Code(完整版):
#include <bits/stdc++.h> using namespace std; int n, m; int w[10000], l[10000], r[10000]; int f[301][301][301]; int dp[310][310]; int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= m; i++) { scanf("%d%d%d", &w[i], &l[i], &r[i]); for (int j = l[i]; j <= r[i]; j++) { f[j][l[i]][r[i]] = w[i]; } } for (int k = 1; k <= n; k++) { for (int i = k; i >= 1; i--) { for (int j = k; j <= n; j++) { if (i != 1) f[k][i - 1][j] = max(f[k][i - 1][j], f[k][i][j]); if (j != n) f[k][i][j + 1] = max(f[k][i][j + 1], f[k][i][j]); } } } for (int i = 1; i <= n; i++) dp[i][i] = f[i][i][i]; for (int len = 2; len <= n; len++) { for (int i = 1; i + len - 1 <= n; i++) { int j = len + i - 1; for (int k = i; k < j; k++) dp[i][j] = max(dp[i][j], dp[i][k] + dp[k + 1][j]); for (int k = i; k <= j; k++) { int val = 0; if (k != i) val += dp[i][k - 1]; if (k != j) val += dp[k + 1][j]; val += f[k][i][j]; dp[i][j] = max(dp[i][j], val); } } } printf("%d", dp[1][n]); return 0; }