背包问题(四种)

背包问题

一、背包模型(定义)

01背包: 有N个物品和一个容量为V的背包,每一个物品有两个属性,一个是它的体积Vi,另一个是它的价值Wi(这里的价值其实是权重的意思),每件物品最多只能用一次(即0次或者1次);问题为从这些物品中挑一些物品,使得总体积 ≤ V,目标是使我们选出的这些物品的总价值最大,最大值是多少?

完全背包:每件物品能用无限次

多重背包:每件物品最多有Si个

分组背包: 物品有N组,每组物品里有若干个,每组里最多选一个物品

注:不一定要装满背包

二、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
题意

在这里插入图片描述

思路

在这里插入图片描述

图解:

状态表示f(i,j):考虑需要几维来表示状态,每个状态的含义是什么

集合:考虑f(i,j)表示哪一个集合,01背包问题中,表示所有满足下列两个条件的选法的集合

属性:01背包问题中,集合中所有选法的价值的最大值

状态计算:如何把每一个f(i,j)状态算出来

f(i,j)的集合划分中的含i的集合的计算:由于所有的选法都包含第i个物品,所以先把第i个物品去掉,然后求最大值,然后再把第i个物品加上(可能为空集:当j<vi时)

注:Dp优化一般来说都是对Dp问题的代码或者计算方程做一个等价变形

二维AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
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=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]<<endl;
	
	return 0;
} 
一维AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
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=m;j>=v[i];j--)
		{
			f[j]=max(f[j],f[j-v[i]]+w[i]);
		}
	}
	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
思路1

在这里插入图片描述

朴素TLE代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
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=0;j<=m;j++)
		{
			for(int k=0;k*v[i]<=j;k++)
			{
				f[i][j]=max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
			}
		}
	}
	cout<<f[n][m]<<endl;
	
	return 0;
}
思路2

在这里插入图片描述

二维AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
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=0;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;
}
一维AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=1010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
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;
}

四、多重背包

多重背包问题I

题目

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
思路

在这里插入图片描述

暴力AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=110;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N],s[N];//v表示体积,w表示价值
int f[N][N];//表示状态

int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>v[i]>>w[i]>>s[i];
	}
	
	for(int i=1;i<=n;i++)
	{
		for(int j=0;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-v[i]*k]+w[i]*k);
			}
		}
	}
	cout<<f[n][m]<<endl;
	
	return 0;
}

多重背包问题II

题目

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

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

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

输入格式

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

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

输出格式

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

数据范围

0 < N ≤ 1000
0 < V ≤ 2000
0 < vi,wi,si ≤ 2000

提示:

本题考查多重背包的二进制优化方法。

输入样例
4 5
1 2 3
2 4 1
3 4 3
4 5 2
输出样例:
10
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=25000,M=2010;

int n,m;//n表示所有物品的个数,m表示容量
int v[N],w[N];//v表示体积,w表示价值
int f[N];//表示状态

int main()
{
	cin>>n>>m;
	int cnt=0;
	
	for(int i=1;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>0)
		{
			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;
	
}

五、分组背包

题目

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
思路

在这里插入图片描述

AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int N=110;

int n,m;//n表示所有物品的个数,m表示容量
int v[N][N],w[N][N],s[N];//v表示体积,w表示价值
int f[N];//表示状态

int main()
{
	cin>>n>>m;
	
	for(int i=1;i<=n;i++)
	{
		cin>>s[i];
		for(int j=0;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=0;k<s[i];k++)
			{
				if(v[i][k]<=j)
				{
					f[j]=max(f[j],f[j-v[i][k]]+w[i][k]);
				}
			}
		}
	}
	
	cout<<f[m]<<endl;
	
	return 0;
	
}
  • 20
    点赞
  • 116
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值