最近在补全提高课所有题目的题解,宣传一下汇总的地方提高课题解补全计划
题目描述
一共有 k 种 物品,对于第 i 种物品,第一维 费用 是 v1i,第二维 费用 是 v2i,价值 是 wi
每个物品只能 被选一次
求一个 选择方案,使得第一维费用 不少于 n,第二维费用 不少于 m 且 总价值最小
分析
本题是一个 二维费用01背包问题 ,但和一般的 二维费用01背包问题 不同
这题要求的是 费用不少于 规定条件,因此我们需要对于 状态的定义 进行改变
具体直接参考以下 闫氏DP分析法
初始状态:f[0][j][k]
其中 j,k≤0
目标状态:f[k][n][m]
Code
时间复杂度: O(k×n×m)
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1010, M = 85;
int n, m, t;
int v1[N], v2[N], w[N];
int f[M][M];
int main()
{
cin >> n >> m >> t;
for (int i = 1; i <= t; ++ i) cin >> v1[i] >> v2[i] >> w[i];
memset(f, 0x3f, sizeof f); //求最小值要把除初始状态以外的所有状态初始化为+∞
f[0][0] = 0; //这里我们把所有j,k小于0的初始状态都合并到f[0][0][0]中来转移,也就是下面的max操作
for (int i = 1; i <= t; ++ i)
{
for (int j = n; j >= 0; -- j)
{
for (int k = m; k >= 0; -- k)
{
f[j][k] = min(f[j][k], f[max(j - v1[i], 0)][max(k - v2[i], 0)] + w[i]);
}
}
}
cout << f[n][m] << endl;
return 0;
}
分析图里面选第
i
个物品的情况忘记加上第i
个物品的价值了……………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………
你是对的,我帮铅笔大佬改一下:

图里面的集合和属性反了吧
确实是
强!!!
想问下为什么最后不用循环所有方案求最小值呢?(y总一开始的错误代码里面最后循环了一遍的原因是啥
状态转移的时候已经自动min了
两次的状态转移方程表示含义不一样
大佬借个图
Orz
真的牛批
聚聚 当前这个dp的初始化 假设是最最朴素的版本,f[i][[j][k]那种的, 是不是只用初始化f[0][j][k]就可以了?
f[0][0][0] = 0, 其他f[0][j][k]都是INF
应该是的
最朴素的代码是这样的:
#include <bits/stdc++.h> using namespace std; const int N = 1010; const int M = 110; int f[N][M][M]; int n, m1, m2; //二维费用01背包-不少于维度费用,求最小代价 int main() { //注意次序 scanf("%d %d %d", &m1, &m2, &n); //求最小值 memset(f, 0x3f, sizeof f); f[0][0][0] = 0; for (int i = 1; i <= n; i++) { int v1, v2, w; scanf("%d %d %d", &v1, &v2, &w); for (int j = 0; j <= m1; j++) for (int k = 0; k <= m2; k++) { //不选择i号物品 f[i][j][k] = f[i - 1][j][k]; //分情况讨论 //物品i加上就够一维使用,此时,只关心二维情况即可 if (j - v1 < 0 && k - v2 >= 0) f[i][j][k] = min(f[i][j][k], f[i - 1][0][k - v2] + w); //物品i加上就够二维使用,此时,只关心一维情况即可 else if (j - v1 >= 0 && k - v2 < 0) f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][0] + w); //如果选择了i号物品,两个维度直接拉满,那么只选择一个i就足够用,它参选的价值是w else if (j - v1 < 0 && k - v2 < 0) f[i][j][k] = min(f[i][j][k], w); else //正常递推 f[i][j][k] = min(f[i][j][k], f[i - 1][j - v1][k - v2] + w); } } printf("%d\n", f[n][m1][m2]); return 0; }
为啥初始化其他得要是正无穷因为求的是最小值吗,那如果求最大值得话初始化为0 或者负无穷吗?
分享一下我的看法,DP如果只有一个起点,那除起点外都要初始化,如果任意状态都可以作为起点,则无需初始化。
这也是我dp分析里经常加入初始状态和目标状态的原因
这是我判断是否要初始化状态的根据
然后关于初始化是什么,是题目而定,目的是为了避免非起点的转移影响目标状态的值
巨巨 ,DP如果只有一个起点 是什么意思? 在本题中是指哪个位置?
初始状态:
f[0][j][k]
其中 j,k≤0然后这题比较特殊,把所有小于 0 的状态合并到 0 进行转移
所以起点只有
f[0][0]
这种类型的题只能定义为至少吗?为什么不能定义为恰好呢,为什么定义为恰好是错的呢?
可以定义成恰好吧,不过需要注意一些边界问题的处理和状态初始化
好的
这里为什么是j,k”小于“0呢
状态定义中,小于0是有实际意义的