蜗牛

一步一步往上爬

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

背包问题

01背包

对第i个物品进行决策,选与不选

状态与选择
状态:背包的容量,可选择的物品
选择:选或不选
f[i][j] :前i个物品容量为j的价值最大值

for(n件物品)

for(容量)

if(容量不够)不选
else max(不选i,选i)

模板题 【01背包问题】

有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。

第 i 件物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
8

include<bits/stdc++.h>

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];
for( int i = 1; i <= n; i++ )
for( int j = 1; j <= m; j++ )
if(j < v[i])
f[i][j] = f[i-1][j];
else
f[i][j] = max(f[i-1][j], f[i-1][j-v[i]]+w[i]);
cout<<f[n][m]<<endl;

return 0;
}

优化 一维数组

f【j】:n件物品容量为j时的最优解

逆序枚举背包容量,否则f[i-1][j]可能被更新为f[i][j]

include<bits/stdc++.h>

using namespace std;

const int N = 1010;

int n, m;
int f[N];
int 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]<<endl;

return 0;
}

优化输入

可以边输入边处理

include<bits/stdc++.h>

using namespace std;

const int N = 1010;

int n, m;
int f[N];

int main()
{
cin>>n>>m;
for( int i = 1; i <= n; i++ ){
int v, w;
cin>>v>>w;
for( int j = m; j >= v; j-- )
f[j] = max(f[j], f[j-v]+w);
}
cout<<f[m]<<endl;

return 0;
}

完全背包问题

有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用。

第 i 种物品的体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 种物品的体积和价值。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000
0<vi,wi≤1000
输入样例
4 5
1 2
2 4
3 4
4 5
输出样例:
10

集合划分:第i个物品选0个,第i个物品选k个
f[i][j] = max(f[i-1][j], f[i][j-v]+w)

朴素版本

include<bits/stdc++.h>

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];
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][j], f[i][j-v[i]]+w[i]);
}
cout<<f[n][m]<<endl;

return 0;
}

优化到一维

include<bits/stdc++.h>

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];
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;
}

同样优化输入

include<bits/stdc++.h>

using namespace std;

const int N = 1010;

int n, m;
int f[N];

int main()
{
cin>>n>>m;
for( int i = 1; i <= n; i++ ){
int v, w;
cin>>v>>w;
for( int j = v; j <= m; j++ )
f[j] = max(f[j], f[j-v]+w);
}
cout<<f[m]<<endl;

return 0;
}

多重背包问题

有 N 种物品和一个容量是 V 的背包。

第 i 种物品最多有 si 件,每件体积是 vi,价值是 wi。

求解将哪些物品装入背包,可使物品体积总和不超过背包容量,且价值总和最大。
输出最大价值。

输入格式
第一行两个整数,N,V,用空格隔开,分别表示物品种数和背包容积。

接下来有 N 行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i 种物品的体积、价值和数量。

输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤100
0<vi,wi,si≤100
输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10

一(当数据范围比较小的时候)

1.可以硬拆成01背包,即把s个相同的物品单独看
cin>>n>>m;
while(n--)
{
cin>>v>>w>>s;
while(s--){
a[++t]=v;//01背包的体积
b[t]=w;//01背包的价值
}
}
for(int i=1;i<=t;i++)
for(int j=m;j>=a[i];j--)
f[j]=max(f[j], f[j-a[i]]+b[i]);

2.三重循环,第三重循环枚举第i个物品从1~s所有的情况

代码如下:

include<bits/stdc++.h>

using namespace std;

const int N = 110;

int n, m;
int f[N];

int main()
{
cin>>n>>m;
for( int i = 1; i <= n; i++ ){
int v, w, s;
cin>>v>>w>>s;
for( int j = m; j >= 0; j-- )
for( int k = 1; k <= s && kv <= j; k++ )
f[j] = max(f[j], f[j-k
v]+k*w);
}
cout<<f[m]<<endl;

return 0;
}

多重背包问题(二进制优化,如果不优化时间复杂度会达到1e9)

正常情况下,一组1024个物品需要枚举1025次,用二进制优化可以<=10次,时间复杂度从O(n^3) -> O(n^2logn)

代码如下
#include<bits/stdc++.h>

define x first

define y second

using namespace std;

typedef pair<int, int> PII;

const int N = 2010;

int n, m;
int f[N];

int main()
{
vector goods;
cin>>n>>m;
for( int i = 1; i <= n; i++ ){
int v, w, s;
cin>>v>>w>>s;
for( int k = 1; k <= s; k=2 ){
s-=k;
goods.push_back({k
v, kw});
}
if(s > 0)
goods.push_back({s
v, s*w});
}
for( auto t:goods){
for( int j = m; j >= t.x; j-- )
f[j] = max(f[j], f[j-t.x]+t.y);
}
cout<<f[m]<<endl;

return 0;
}

分组背包问题

有 N 组物品和一个容量是 V 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。

求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。

输出最大价值。

输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。

接下来有 N 组数据:

每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤100
0<Si≤100
0<vij,wij≤100
输入样例
3 5
2
1 2
2 4
1
3 4
1
4 5
输出样例:
8

for( int i = 1; i <= n; i++ )

for( int j = m; j >= v; j-- )

f[j] = max(f[j], f[j-v[0]]+w[0], f[j-v[1]]+w[1], ..., f[j-v[s-1]]+w[s-1]);

代码如下:

include<bits/stdc++.h>

using namespace std;

const int N = 110;

int n, m;
int f[N];
int v[N], w[N];

int main()
{
cin>>n>>m;
for( int i = 1; i <= n; i++ ){
int s;
cin>>s;
for( int j = 1; j <= s; j++ )
cin>>v[j]>>w[j];
for( int j = m; j >= 0; j-- )
for( int k = 1; k <= s; k++ )
if(j >= v[k])
f[j] = max(f[j], f[j-v[k]]+w[k]);
}
cout<<f[m]<<endl;

return 0;
}

posted on 2021-10-01 18:25  对影丶成三人  阅读(44)  评论(0)    收藏  举报