序言:
当你自始至终都用孩子可以理解的简单的语言写出一个想法,那么你便迫使自己在更深层次上理解了该概念,并简化了观点之间的关系和联系。
--《费曼学习法》
~~~如有错误,还请斧正
二维
问题导入:你是小跳,你得到了做梦的奖励,可以用一个特定的背包装糖果,你又最爱吃甜,但是呢,背包大小有限,现在给出糖果的大小和甜度,你想知道你能获得的甜度最大的值。
我将尽可能地叙述(其实我也不会数学证明)
· 拿不拿当前位置的糖果对我们有什么影响取决于之前的状态
开一个f[N][N]。一维表示糖果数,第二维表示空间大小
· 不拿,背包中得信息就是之前的信息 f[i-1][j]
·拿,假如此颗糖果过大,那么我们应该从一个刚好能拿下这颗糖果的状态转移过来,也就是空间为j-v[i]才能保证不会超过大小,所以f[i-1][j-v[i]]+w[i],但是你拿下这颗糖果不一定比不拿甜度更大哦,所以这时候应该与自身f[i][j]取一个max
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int f[N][N];
//n是个数,m是背包大小,v[]糖果大小.w[]是糖果甜度
int n,m,v[N],w[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
{
f[i][j]=f[i-1][j];
if(j>=v[i])
f[i][j]=max(f[i][j],f[i-1][j-v[i]]+w[i]);
}
cout<<f[n][m];
return 0;
}
一维优化
先放代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N=100;
int f[N];
//n是个数,m是背包大小,v[]糖果大小.w[]是糖果甜度
int n,m,v[N],w[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>v[i]>>w[i];
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]);
cout<<f[m];
return 0;
}
第一个问题:为什么是j>=v[i],因为我们要从不拿这个糖果的背包大小状态转移过来,其实也可以在循环中加上判断
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 100;
int f[N];
//n是个数,m是背包大小,v[]糖果大小.w[]是糖果甜度
int n, m, v[N], w[N];
int main()
{
cin >> n >> m;
for(int i = 1; i <= n; i++)cin >> v[i] >> w[i];
for(int i = 1; i <= n; i++)
for(int j = m; j >= 0; j--)
if(j >= v[i])
f[j] = max(f[j], f[j - v[i]] + w[i]);
cout << f[m];
return 0;
}
第二个问题:为什么第二层循环倒着?
- 如果从前往后开始枚举,那么试想象这样一种i=3的情况带入f[j] = max(f[j], f[j - v[i]] + w[i]);就可能存在f[4]=f[3]+w[3],f[3]=f[1]+w[3]这样的情况,计算得f[4]=f[1]+w[3]+w[3],就会导致一个糖果被重复的取。(完全背包问题因为糖果可以无限取,所以从前往后枚举哦)
- 正是因为我们由于的状态是由于之前小空间的不断叠加而导致的重复,那么我们直接枚举大空间从后往前枚举就不会收到之前的影响啦