int n; // 物品总数 int m; // 背包容量 int v[N]; // 重量 int w[N]; // 价值
int f[N][M]; for (int i = 1; i <= n; i ++ ) for (int j = 1; j <= m; j ++ ) for (int k = 0; k *v[i] <= j; k ++ ) f[i][j] = max(f[i][j], f[i - 1][j - k * v[i]] + k* w[i]);
cout << f[n][m];
优化后(公式推出)
1 2 3 4 5 6 7 8
int f[N][M]; for (int i = 1; i <= n; i ++ ) for(int j = 1; j <= m; j ++ ) if (j < v[i]) f[i][j] = f[j - 1][j]; else f[i][j] = max(f[i -1][j], f[i][j - v[i]] + w[i]); // 注意!这里和 01 背包的脚标有区别
cout << f[n][m]
类似于 01 背包,也能转换为一维形式
1 2 3 4 5 6 7
int f[M]; for (int i = 1; i <= n; i ++ ) for(int j = v[i]; j <= m; j ++ ) f[j] = max(f[j], f[j - v[i]] + w[i]); // 仍然是正序!
int n; // 物品总数 int m; // 背包容量 int v[N]; // 重量 int w[N]; // 价值
// 读入物品个数时重新打包 int a, b, s; cin >> a >> b >> s; // 该物品的重量、价值、最大数量 int k = 1; // 当前包裹大小 while (k <= s){ cnt ++; // 实际物品种数 v[cnt] = a * k; w[cnt] = b * k; s -= k; k *= 2; }
if (s > 0){ // 不足的补足到大小为 C 的包裹 cnt ++; v[cnt] = a * s; w[cnt] = b * s; }
n = cnt; // 更新物品种数
// 01 背包问题 for (int i = 1; i <= n; i ++ ) for (int j = m; j >= v[i]; j -- ) f[j] = max(f[j], f[j - v[i]] + w[i]);
int n; // 物品总数 int m; //背包容量 int v[N][S]; // 重量 int w[N][S]; // 价值 int s[N]; // 各组物品种数
// 读入数据 for (int i = 1; i <= n; i ++ ){ cin >> s[i]; for (int j = 1; j <= s[i]; j ++ ) cin >> v[i][j] >> w[i][j]; }
for (int i = 1; i <= n; i ++ ) for (int j = m; j >= 1; j -- ) for (int k = 1; k <= s[i]; k ++ ) if (v[i][k] < j) f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
cout << f[m];
线性 DP
线性 DP:状态之间有线性关系的动态规划问题
没有比较固定的模板
数字三角形
T:898. 数字三角形
DP 分析:
最长上升子序列
T:895. 最长上升子序列
DP 分析:
- 贪心优化:
- 二分版:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
int len = 0; // 最长上升子序列 q[] 长度 for (int i = 0; i < n; i ++ ) { int l = 0, r = len; while (l < r) { int mid = l + (r - l) / 2; if (q[mid] < a[i]) l = mid; else r = mid + 1; } q[r ++ ] = a[i]; len = max(len, r); } cout << len << endl;
stl 版
1 2 3 4 5 6 7 8 9 10
vector<int> stk; //类似于单调栈 stk.push_back(arr[0]);
for (int i = 1; i < n; i ++ ) { if (arr[i] > stk.back()) stk.push_back(arr[i]); // 如果该元素大于栈顶元素,将该元素入栈 else *lower_bound(stk.begin(), stk.end(), arr[i]) = arr[i]; // 替换掉第一个大于或者等于这个数字的那个数 } cout << stk.size() << endl;
所有的区间 DP 问题,第一维都是枚举区间长度,一般 len = 1 用来初始化,枚举从 len = 2 开始,第二维枚举起点i (右端点 j 自动获得,j = i + len - 1)
1 2 3 4 5 6 7 8 9 10
for (int i = 1; i <= n; i++) { dp[i][i] = default_value; } for (int len = 2; len <= n; len++) //区间长度 for (int i = 1; i + len - 1 <= n; i++) { //枚举起点 int j = i + len - 1; //区间终点 for (int k = i; k < j; k++) { //枚举分割点,构造状态转移方程 // ... } }