背包问题分类
两维优化为一维,使用的是i-1就从大到小,使用i就从小到大。
1.01背包 (每个物品最多用一次)
初始版代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
//f[0][0~m] = 0
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j] = f[i-1][j];
if(j >= v[i]) f[i][j] = max(f[i-1][j], f[i-1][j-v[i]] + w[i]);
}
}
cout<<f[n][m]<<endl;
return 0;
}
优化(代码的等价变形,变成一维):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
//f[0][0~m] = 0
for(int i=1;i<=n;i++)
{
for(int j=m;j>=v[i];j--)
{
//f[j] = f[j];
f[j] = max(f[j], f[j-v[i]] + w[i]);
}
}
cout<<f[m]<<endl;
return 0;
}
2.完全背包问题(每个物品无限个)
朴素版代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
//f[0][0~m] = 0
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]<<endl;
return 0;
}
优化思路:枚举
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
//f[0][0~m] = 0
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
f[i][j] = f[i-1][j];
if(j >= v[i]) f[i][j] = max(f[i-1][j], f[i][j - v[i]] + w[i]);
}
}
cout<<f[n][m]<<endl;
return 0;
}
一维优化(代码变形):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1010;
int n, m;
int v[N], w[N];
int f[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
//f[0][0~m] = 0
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]);
}
}
cout<<f[m]<<endl;
return 0;
}
3.多重背包问题(每个物品有限定选择次数)
解法与完全背包相同
代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int v[N], w[N], s[N];
int f[N][N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
//f[0][0~m] = 0
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
for(int k=0;k<s[i] && 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]<<endl;
return 0;
}
二进制优化:
递推式
多了最后一项是因为,完全背包问题容量最大是k,但是多重背包不是,它是被个数限制,现在最大容量是j-v,我仍然可以选到s个第i个物品,只要它容量没超的情况下。所以优化不能往这方面来。我们考虑其他优化
对于1023个的si来说,我们不用枚举1023次,可以将这些物品打包成10组,这十组任意选法都可以凑出0~1023的任意数。如果si为200,我们可以有1,2,4,8,16,32,64,73.因为127加73等于200.
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 25000, M = 2010;
int n ,m;
int v[N], w[N];
int f[N];
int main()
{
cin>>n>>m;
int cnt = 0;
for(int i=0;i<n;i++)
{
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)
{
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
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]<<endl;
return 0;
}
4、分组背包(每一组物品只能选一种)
代码(直接写一维):
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 110;
int n, m;
int v[N][N], w[N][N], s[N];
int f[N];
int main()
{
cin>>n>>m;
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>=0;j--)
{
for(int k=1;k<=s[i];k++)
{
if(j >= v[i][k]) f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
}
}
}
cout<<f[m]<<endl;
return 0;
}