acwing 背包问题

本文详细介绍了四种经典的背包问题,包括0-1背包、完全背包、多重背包和分组背包,并提供了相应的动态规划解决方案。通过实例解析了每种问题的输入格式、输出格式及解题思路,展示了AC代码,帮助读者深入理解动态规划在背包问题中的应用。
摘要由CSDN通过智能技术生成

1.背包问题

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

第 ii 件物品的体积是 vivi,价值是 wiwi。

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

输入格式:

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

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

输出格式:

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

数据范围:

0<N,V<=1000

0<vi,wi<=1000

ac代码:

#include<stdio.h>
#include<stdlib.h>
int n,v;
int vi[1010],wi[1010];
int dp[1010];
int main(){
	int i=1,j;
	scanf("%d%d",&n,&v);
	while(i<=n){
		scanf("%d %d",&vi[i],&wi[i]);//vi表示重量,wi表示价值 
		i++;
	}
	for(i=1;i<=n;i++){
		for(j=v;j>0;j--){//表示现在还可以放入的重量 
			if(j>=vi[i])
			//如果还可以放入的重量大于等于第i个物品的重量
			//则可以考虑把物品i放入 
				if(dp[j]<dp[j-vi[i]]+wi[i])//如果当前可放入j重量条件下,
				//背包的价值小于当前背包重量减掉物品i重量后的价值加上物品i的质量
				//则修改背包的j的价值 
					dp[j]=dp[j-vi[i]]+wi[i];
		}
	}
	//输出当背包可容纳重量为v的时候的最大价值 
	printf("%d\n",dp[v]);
	return 0;
}

2.完全背包问题

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

第 ii 种物品的体积是 vivi,价值是 wiwi。

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

输入格式:

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

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

输出格式:

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

数据范围:

0<N,V<=1000

0<vi,wi<=1000

思路整理:

ac代码:

#include<stdio.h>
#include<stdlib.h>
int n,v;
int vi[1010],wi[1010];
int dp[1010];
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i=1,j;
	scanf("%d%d",&n,&v);
	while(i<=n){
		scanf("%d %d",&vi[i],&wi[i]);//vi表示重量,wi表示价值 
		i++;
	}
	for(i=1;i<=n;i++){
		for(j=1;j<=v;j++){
			dp[j]=dp[j];
			if(j>=vi[i]){
				dp[j]= max(dp[j],dp[j-vi[i]]+wi[i]);
			}
		}
	} 
	printf("%d\n",dp[v]);
	return 0;
}

3.多重背包问题

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

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

输入格式:

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

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

输出格式:

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

数据范围:

0<N,V<=1000

0<vi,wi,si<=1000

思路:

        强拆,在数据较小情况下,直接把数量转化为单个有体积价值的物品

ac代码:

#include<stdio.h>
#include<stdlib.h>
#define N 10010
int n,v,w,k,s;
int vi[N],wi[N];
int dp[N];
int t;
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	int i=1,j;
	scanf("%d%d",&n,&v);
	while(i<=n){
		scanf("%d%d%d",&k,&w,&s);
		while(s--){
			vi[++t]=k;
			wi[t]=w;
		}
		i++;
	}
	for(i=1;i<=t;i++){
		for(j=v;j>=vi[i];j--){
				dp[j]= max(dp[j],dp[j-vi[i]]+wi[i]);
		}
	} 
	printf("%d\n",dp[v]);
	return 0;
}
#include <iostream>
using namespace std;

const int N = 110;		// 数组的大小,取值稍大于数据范围即可

int n, m;	// 变量 n,m 来获取物品的数量以及背包的容量
int f[N];		// 一维数组 dp[j] 表示背包的容量为 j 时所能获取的最大价值

int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)	// i 循环让考虑物品的数量不断增加
	{
		int v, w, s;	// 三个变量 v,w,s 分别获取每个物品的体积、价值与数量,在循环内部边更新边处理
		cin >> v >> w >> s;
		for (int j = m; j >= v; j--)	// j 循环让背包的最大容量不断增加
		{
			for (int k = 1; k <= s && k * v <= j; k++)	
			// k 循环让选用当前物品的数量不断增加,k不能大于物品最大数量s,k * v不能大于当前循环的背包容量 j
			{
				f[j] = max(f[j], f[j - v * k] + w * k);
			}
		}
	}
	cout << f[m] << endl;
	return 0;
}

4.多重背包问题2

有 NN 种物品和一个容量是 VV 的背包。

第 ii 种物品最多有 sisi 件,每件体积是 vivi,价值是 wiwi。

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

输入格式:

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

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

输出格式:

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

数据范围:

0<N10000<N≤1000
0<V20000<V≤2000
0<vi,wi,si2000

 

#include <stdio.h>
#include <stdlib.h>
#define N 12000
#define M 2010
int n,m;
int vi[N],wi[N];
int dp[N];
int max(int a,int b){
	return a>b?a:b;
} 
int main(){
	int a,b,s,i,j,k;
	scanf("%d %d",&n,&m);
	int cnt =0; 
	for(i=1;i<=n;i++){
		scanf("%d %d %d",&a,&b,&s);
		k=1;
		while(k<=s){
			vi[++cnt] = a*k;
			wi[cnt] = b*k;
			s-=k;
			k*=2;
		}
		if(s>0){
			vi[++cnt] = a*s;
			wi[cnt] = b*s;
		}
	}
	n=cnt;//更新n 
	for(i=1;i<=n;i++){
		for(j=m;j>=vi[i];j--){
			dp[j] = max(dp[j],dp[j-vi[i]]+wi[i]);
		}
	}
	printf("%d",dp[m]);
	return 0;
}

5.分组背包问题

有 NN 组物品和一个容量是 VV 的背包。

每组物品有若干个,同一组内的物品最多只能选一个。
每件物品的体积是 vijvij,价值是 wijwij,其中 ii 是组号,jj 是组内编号。

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

输出最大价值。

输入格式:

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

接下来有 NN 组数据:

  • 每组数据第一行有一个整数 SiSi,表示第 ii 个物品组的物品数量;
  • 每组数据接下来有 SiSi 行,每行有两个整数 vij,wijvij,wij,用空格隔开,分别表示第 ii 个物品组的第 jj 个物品的体积和价值;

输出格式:

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

数据范围:

0<N,V1000<N,V≤100
0<Si1000<Si≤100
0<vij,wij100

ac代码:

#include <stdio.h>
#define N 110
int n,m;
int i,j,k;
int dp[N],vi[N],wi[N];
int max(int a,int b){
	return a>b?a:b;
}
int main(){
	scanf("%d %d",&n,&m);
	while(n--){//分组 
		scanf("%d",&k);
		//输入组内的每一个物品的价值和体积 
		for(i=0;i<k;i++)
			scanf("%d %d",&vi[i],&wi[i]);
		for(j=m;j>=0;j--){//把关于体积变化循环放在外面便于输出每次分组的最大值
		//同时,不能只一个循环 查找dp【m】是因为多重分组,因此第一组的最优解和第二组的搭配在一起可能不如
		//其他的解搭配,所以需要遍历初始化,dp【j】 
			for(i=0;i<k;i++){
					if(j>=vi[i])
						dp[j] = max(dp[j],dp[j-vi[i]]+wi[i]);
					printf("dp[%d] = %d",j,dp[j]);
				}
			printf("\n");
		}
			
			
	}
	printf("%d\n",dp[m]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值