[[TOC]]
问题引入
问题描述
给定
输入格式/样例
格式:第一行有两个数
5 7
6 5
5 4
2 3
4 6
2 6
解法1,
设
现在考虑最后一个物品
- 集合
表示 没有出现(一个也不选),这个时候 ,对应的答案就是 . - 集合
表示 有出现, 然后在根据物品 出的次数,对集合 进行分类
- 可能出现
次,设为 ,转化对应的问题为 - 可能出现
次,设为 ,转化对应的问题为 - 最多可能出现
次,设为 . 显然 ,转化对应的问题为
显然,可以得出
于是我们得到一般的状态转移方程
那么这样的话,我们可以得到一个
点击
//Author by [Rainboy](https://github.com/rainboylvx)
//date: 2024-07-09 22:47:39
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,m;
int w[maxn];
int v[maxn];
int f[maxn][maxn];
int main (int argc, char *argv[]) {
std::cin >> n >> m;
for(int i = 1;i <= n ;++i ) // i: 1->n
{
cin >> w[i] >> v[i];
}
for(int i = 1;i <= n ;++i ) // i: 1->n
{
for(int j = 1;j <= m ;++j ) // j: 1->m
{
//表示一件物品i也不选,可以理解成k=0
f[i][j] = f[i-1][j];
//选k件,
for(int k = 1 ; k*w[i] <= j ;k++) {
f[i][j] = max(f[i][j],f[i-1][j- k*w[i]] + k*v[i]);
}
}
}
cout << f[n][m] <<endl;
return 0;
}
解法2, 转成01背包
很容易想到,虽然题目说:每个物品有无限个.但因为容量是有限的,所以不可以无限的去放某个物品
于是我们成功的把问题转化成了一个01背包问题.
如果要用二维数组存储
因为01背包的一维写法不需要开二维数组,那就是不需要计算行数了,减少了计算量.所以这里使用01背包的一维写法.
得到一个
点击
//Author by [Rainboy](https://github.com/rainboylvx)
//date: 2024-07-09 22:47:39
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,m;
int w[maxn];
int v[maxn];
int f[maxn]; //一维数组
int main (int argc, char *argv[]) {
std::cin >> n >> m;
for(int i = 1;i <= n ;++i ) // i: 1->n
{
cin >> w[i] >> v[i];
}
for(int i = 1;i <= n ;++i ) // i: 1->n
{
// 遍历 k个 物品,每个物品的重量都是(w_i,v_i)
for (int k = 1; k * w[i] <= m; k++)
{
// 上两行都是在枚举物品
//倒过来枚举容量
for(int j = m;j>= w[i];j--) {
f[j] = max(f[j],f[j-w[i]] + v[i]);
}
}
}
cout << f[m] <<endl;
return 0;
}
解法3优化,
显然: 集合
选
在
我们成功应用了集合一一映射的思想,把一个问题转成了另一个问题.
//Author by [Rainboy](https://github.com/rainboylvx)
//date: 2024-07-09 22:47:39
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,m;
int w[maxn];
int v[maxn];
int f[maxn][maxn];
int main (int argc, char *argv[]) {
std::cin >> n >> m;
for(int i = 1;i <= n ;++i ) // i: 1->n
{
cin >> w[i] >> v[i];
}
for(int i = 1;i <= n ;++i ) // i: 1->n
{
for(int j = 1;j <= m ;++j ) // j: 1->m
{
f[i][j] = f[i-1][j]; //一件i也不选
//可以至少选一件
if( w[i] <= j && f[i][j] < f[i][j-w[i]] + v[i])
f[i][j] = f[i][j-w[i]] + v[i];
}
}
cout << f[n][m] <<endl;
return 0;
}
优化2,降维
仔细观察动画,类比[
Rbook: 01背包]的思想,根据每个点需要的转移点的位置,可以把
//Author by [Rainboy](https://github.com/rainboylvx)
//date: 2024-07-09 22:47:39
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+5;
int n,m;
int w[maxn];
int v[maxn];
int f[maxn];
int main (int argc, char *argv[]) {
std::cin >> n >> m;
for(int i = 1;i <= n ;++i ) // i: 1->n
{
cin >> w[i] >> v[i];
}
for(int i = 1;i <= n ;++i ) // i: 1->n
{
for(int j = w[i];j <= m ;++j ) // j: 1->m
{
f[j] = max(f[j],f[j-w[i]] +v[i]);
}
}
cout << f[m] <<endl;
return 0;
}
练习题目
- luogu-P1616 疯狂的采药 入门题目
暂无题目