1. 题目来源
链接:3499. 序列最大收益
相关题目:[线性dp] 最长上升子序列(模板题+最长上升子序列模型)
2. 题目解析
很不错的一道 dp
问题,关键是状态定义与状态转移以及发现性质后的相关优化。
在状态转移中,是以倒数第二个数是哪个进行集合划分的,这点与 LIS
最长上升子序列问题的状态划分非常像,所以本题可以说是 LIS
问题的变种题目。
分析题目性质可以发现,第一个数和最后一个数被选一定不会使得最优解变差,所以可以直接将第一个数初始化,求解答案时,只需要循环求最后一个数即可。
时间复杂度:
O
(
n
3
)
O(n^3)
O(n3)
空间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 205;
int n, k, m;
int a[N], w[N][N];
int f[N][N]; // f[i][j] 考虑前 i 个数且删除 j 个并将 i 保留的最大价值
int main() {
scanf("%d%d%d", &n, &k, &m);
for (int i = 1; i <= m; i ++ ) scanf("%d", &a[i]);
for (int i = 1; i <= n; i ++ )
for (int j = 1; j <= n; j ++ )
scanf("%d", &w[i][j]);
memset(f, 0xc0, sizeof f);
f[1][0] = 0;
for (int i = 2; i <= m; i ++ )
for (int j = 0; j <= k; j ++ )
for (int u = 1; u < i; u ++ ) // u = 0, = 1 均可,但是 1 初始化被选,那么倒数第二个数一定是从 1 开始的,就不存在空集情况了
if (j >= i - u - 1)
f[i][j] = max(f[i][j], f[u][j - (i - u - 1)] + w[a[u]][a[i]]);
int res = 0;
for (int i = 0; i <= k; i ++ )
res = max(res, f[m][i]);
cout << res << endl;
return 0;
}