背包问题思考

本文介绍了经典的01背包问题,以及如何通过动态规划解决。此外,还涉及到了数字组合背包问题,即在给定数值集合中寻找组合使得和等于特定值的方案数。最后,讨论了带有主件和附件的背包问题,需要考虑物品之间的依赖关系。所有问题均通过动态规划求解最大价值或方案数。
摘要由CSDN通过智能技术生成

经典的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

使用一个二维数组dp[i][j],表示的是选取第i个物品不超过j容积的最大价值,在遍历第i个物品时,dp[i][j]==max(前一个的value,前一个value加上本次的value),前提是加上了本次的value,不超过j容积的。可以列出公式。

#include<bits/stdc++.h>
#define LL long long
#define PP pair<int, int>
using namespace std;

const int N=1010;
int v[N],w[N]; //v是各背包容积,w是价值 
int f[N][N];  //DP数组 

int main() {
	ios::sync_with_stdio(0);
	int n,m;
	cin>>n>>m;   //有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];       //满足为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;  //输出为m容积可以发的最大价值 
	cin.tie(0);
	cout.tie(0);
	
	return 0;
}

用滚动数组来优化,从后往前遍历背包容积,同时确保j>=v[i],才可以加入value值,为什么是逆序呢,因为要更新第j位置就会需要j-v[i],再更新j-v[i]就需要j,导致j是已经是更新的了,这是错误的

//滚动数组优化版本 
#include<iostream>
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 = m; j >= v[i]; j--) {  //从后往前遍历j要求不超过其容积 
            f[j]=max(f[j],f[j-v[i]]+w[i]);
        }
    }

    cout << f[m] << endl;
 return 0;    
}

数字组合背包问题(分组背包,有依赖的背包)

给定 N 个正整数 A1,A2,…,AN,从中选出若干个数,使它们的和为 M,求有多少种选择方案。

输入格式第一行包含两个整数 N和 M。第二行包含 N个整数,表示 A1,A2,…,AN。输出格式包含一个整数,表示可选方案数。

数据范围1≤N≤100  1≤M≤10000  1≤Ai≤1000
答案保证在 int 范围内。

输入样例:

4 4
1 1 2 2

输出样例:

3

用数组dp[j],表示和为j的选择方案。f(i,j)表示第i个整数时不超过j的选取方案,当遍历第i个整数时,选取第i个整数时为f(i-1,j-a[i]) ,不选第i个整数为f(i-1,j),最后加上f(i,j)

#include<bits/stdc++.h>
using namespace std;

const int n=101;
int a[n];
int f[10001];

int main(){
    int N,M;
    cin>>N>>M;
    for(int i=1;i<=N;i++)  cin>>a[i];
    f[0]=1;    //选取第0个元素只有一种选择方案
    for(int i=1;i<=N;i++){
        for(int j=M;j>=a[i];--j){  //满足a[i]不超过j
            f[j]+=f[j-a[i]];
        }
    }
    cout<<f[M]<<endl;
    return 0;
}

开心的金明

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。

更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。

今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的N 元。

于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1∼5 表示,第 5 等最重要。

他还从因特网上查到了每件物品的价格(都是整数元)。

他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第 j件物品的价格为v[j],重要度为 w[j],共选中了 k 件物品,编号依次为 j1,j2,…,jk,则所求的总和为: v[j1]×w[j1]+v[j2]×w[j2]+…+v[jk]×w[jk]

请你帮助金明设计一个满足要求的购物单。输入格式:输入文件的第 1 行,为两个正整数 N 和 m,用一个空格隔开。(其中 N 表示总钱数,m 为希望购买物品的个数) 从第 2 行到第 m+1 行,第 jj 行给出了编号为j−1 的物品的基本数据,每行有 2 个非负整数 v和 p。(其中 v表示该物品的价格,p 表示该物品的重要度)

输出格式:输出文件只有一个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值(数据保证结果不超过 108)。

数据范围1≤N<30000
1≤m<25
0≤v≤10000
1≤p≤5

输入样例:

1000 5
800 2
400 5
300 5
400 3
200 2

输出样例:

3900

典型的01背包问题,不同的是f[j]表示的是价格和重要度的乘奇。

#include<iostream>
using namespace std;

const int n=30010;
int v[n],w[n]; //v价格 w等级
int f[n];

int main(){
    int N,m;
    cin>>N>>m;  //N总价格,m个数
    for(int i=1;i<=m;i++)  cin>>v[i]>>w[i];
    
    for(int i=1;i<=m;i++){
        int temp=w[i]*v[i];
        for(int j=N;j>=v[i];j--){
            f[j]=max(f[j],f[j-v[i]]+temp);
        }
    }
    cout<<f[N]<<endl;
}

金明的预算

金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间金明自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 nn 元钱就行”。今天一早,金明就开始做预算了,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:

主件附件
电脑打印机,扫描仪
书柜图书
书桌台灯,文具
工作椅

如果要买归类为附件的物品,必须先买该附件所属的主件。每个主件可以有 0 个、1 个或 2 个附件。每个附件对应一个主件,附件不再有从属于自己的附件。金明想买的东西很多,肯定会超过妈妈限定的 n元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数1∼5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是 10 元的整数倍)。他希望在不超过 n 元的前提下,使每件物品的价格与重要度的乘积的总和最大。

设第 j 件物品的价格为 vj​,重要度为wj​,共选中了 k 件物品,编号依次为 j1,j2,jk,则所求的总和为:vj*wj 求和(每个k加起来)

请你帮助金明设计一个满足要求的购物单。

输入格式

第一行有两个整数,分别表示总钱数 n 和希望购买的物品个数 m。

第 2到第 (m+1) 行,每行三个整数,第 (i+1) 行的整数vi​,pi​,qi​ 分别表示第 i 件物品的价格、重要度以及它对应的的主件。如果 qi​=0,表示该物品本身是主件。

输入:

1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0

输出:

2200

这道背包问题有主件和附件之分,在确定要不要选取该物品时,存在判断。把每种情况都考虑到即可。1、都是主件。2、主件和附件1。3、主件和附件2。4、主件和附件1和附件2。每次输入进行判断,保存该主件的附件个数,以及对于的v和v*p

#include<bits/stdc++.h>
#define LL long long
#define PP pair<int, int>
using namespace std;

const int N=32001;
int mw[33333],mv[33333],fw[33333][3],fv[33333][3],f[33333],v,p,q;
//mw是价格 mv是乘奇 

int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>v>>p>>q;
		if(!q){ //主件 
			mw[i]=v;
			mv[i]=v*p; 
		}
		else{
			fw[q][0]++;  //记录主件q的附件个数
			fw[q][fw[q][0]]=v; 
			fv[q][fw[q][0]]=v*p;
		}
	}
	
	for(int i=1;i<=m;i++){
		for(int j=n;j>=mw[i];j--){
			f[j]=max(f[j],f[j-mw[i]]+mv[i]);  //只要主件
			if(j>=mw[i]+fw[i][1]) //要主件和附件1 
				f[j]=max(f[j],f[j-mw[i]-fw[i][1]]+mv[i]+fv[i][1]);
			if(j>=mw[i]+fw[i][2]) //要主件和附件2 
				f[j]=max(f[j],f[j-mw[i]-fw[i][2]]+mv[i]+fv[i][2]);
			if(j>=mw[i]+fw[i][1]+fw[i][2]) //要主件和附件1和2 
				f[j]=max(f[j],f[j-mw[i]-fw[i][1]-fw[i][2]]+mv[i]+fv[i][1]+fv[i][2]);
		}
	}
	cout<<f[n]<<endl;
} 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Libert_AC

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值