序言
背包算法算是常见的算法题,CSDN比赛中也见到多次,基本就是二维数组求解,用排列组合呢?通过枚举找到最大值肯定是可行的,算法一般不用,毕竟时间复杂度比背包算法大。第14期的CSDN第一题就是找出缺失的枚举项,我觉得还是有必要熟悉下。
背包算法只是动态规划中的一种算法,解题肯定要跟场景相匹配,排列组合算是暴力求解,所有情况都列举出来了,难道还没有最优值。
常规题
已知存在n个宝物,每个宝物都有自己的质量m和价值v,在考虑选择宝物时只能选择总质量小于等于M的方案,请问在最优方案下选择宝物,能获取到最大价值V是多少?
物品信息如上图,使用背包算法就要建个二维表
先尝试放A,当空间>=1时有效值就是1500
数据有点不好
看最后一格 C4
如果放C,4格占满,价值只有3000,没有上一行B4数据大,所以填较大值3500
1(1500),3(2000),4(3000),3500
1(1500),4(3000),3(2000),1500
3(2000),1(1500),4(3000),3500
3(2000),4(3000),1(1500),2000
4(3000),3(2000),1(1500),3000
4(3000),1(1500),3(2000),3000
3500
排列组合数据如上,从前到后依次尝试添加物品
第一行只能放前2个,总价值就3500
第二行只能放第一个,总价值就1500
最后结果就是3500
变种题
金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 N 元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 N 元。于是,他把每件物品规定了一个重要度,分为 5 等:用整数 1-5 表示,第 5 等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过 N 元(可以等于 N 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。 设第 j 件物品的价格为 v_[j] ,重要度为 w_[j] ,共选中了 k 件物品,编号依次为 j_1,j_2,…,j_k ,则所求的总和为: v_[j_1] imes w_[j_1]+v_[j_2] imes w_[j_2]+ …+v_[j_k] imes w_[j_k] 。 请你帮助金明设计一个满足要求的购物单。
区别就是价值的算法,这边是乘积
注意,这里的颗粒度是1,不要认为都是100的倍数,测试数据不都是100的倍数,不要被骗了。
800(2),400(5),300(5),400(3),200(2), use1:1200 val:1600
800(2),300(5),400(5),400(3),200(2), use1:1100 val:1600
800(2),400(3),300(5),400(5),200(2), use1:1200 val:1600
800(2),200(2),300(5),400(3),400(5), use1:1000 val:2000
400(5),800(2),300(5),400(3),200(2), use1:1200 val:2000
400(5),300(5),800(2),400(3),200(2), use1:1500 val:3500
400(5),300(5),400(3),800(2),200(2), use1:1100 val:3500
400(5),300(5),200(2),400(3),800(2), use1:1300 val:3900
400(5),300(5),200(2),800(2),400(3), use1:1700 val:3900
400(5),400(3),300(5),800(2),200(2), use1:1100 val:3200
400(5),400(3),800(2),300(5),200(2), use1:1600 val:3200
400(5),400(3),200(2),800(2),300(5), use1:1000 val:3600
400(5),200(2),300(5),400(3),800(2), use1:1300 val:3900
400(5),200(2),300(5),800(2),400(3), use1:1700 val:3900
400(5),200(2),400(3),300(5),800(2), use1:1000 val:3600
400(5),200(2),800(2),400(3),300(5), use1:1400 val:2400
300(5),400(5),800(2),400(3),200(2), use1:1500 val:3500
300(5),400(5),400(3),800(2),200(2), use1:1100 val:3500
300(5),400(5),200(2),400(3),800(2), use1:1300 val:3900
300(5),400(5),200(2),800(2),400(3), use1:1700 val:3900
300(5),800(2),400(5),400(3),200(2), use1:1100 val:1500
300(5),400(3),800(2),400(5),200(2), use1:1500 val:2700
300(5),400(3),400(5),800(2),200(2), use1:1100 val:2700
300(5),400(3),200(2),400(5),800(2), use1:1300 val:3100
300(5),400(3),200(2),800(2),400(5), use1:1700 val:3100
300(5),200(2),800(2),400(3),400(5), use1:1300 val:1900
300(5),200(2),400(3),800(2),400(5), use1:1700 val:3100
300(5),200(2),400(3),400(5),800(2), use1:1300 val:3100
300(5),200(2),400(5),400(3),800(2), use1:1300 val:3900
300(5),200(2),400(5),800(2),400(3), use1:1700 val:3900
400(3),400(5),300(5),800(2),200(2), use1:1100 val:3200
400(3),400(5),800(2),300(5),200(2), use1:1600 val:3200
400(3),400(5),200(2),800(2),300(5), use1:1000 val:3600
400(3),300(5),400(5),800(2),200(2), use1:1100 val:2700
400(3),300(5),800(2),400(5),200(2), use1:1500 val:2700
400(3),300(5),200(2),800(2),400(5), use1:1700 val:3100
400(3),300(5),200(2),400(5),800(2), use1:1300 val:3100
400(3),800(2),300(5),400(5),200(2), use1:1200 val:1200
400(3),200(2),300(5),800(2),400(5), use1:1700 val:3100
400(3),200(2),300(5),400(5),800(2), use1:1300 val:3100
400(3),200(2),800(2),300(5),400(5), use1:1400 val:1600
400(3),200(2),400(5),800(2),300(5), use1:1000 val:3600
200(2),400(5),300(5),400(3),800(2), use1:1300 val:3900
200(2),400(5),300(5),800(2),400(3), use1:1700 val:3900
200(2),400(5),400(3),300(5),800(2), use1:1000 val:3600
200(2),400(5),800(2),400(3),300(5), use1:1400 val:2400
200(2),300(5),400(5),400(3),800(2), use1:1300 val:3900
200(2),300(5),400(5),800(2),400(3), use1:1700 val:3900
200(2),300(5),400(3),400(5),800(2), use1:1300 val:3100
200(2),300(5),400(3),800(2),400(5), use1:1700 val:3100
200(2),300(5),800(2),400(3),400(5), use1:1300 val:1900
200(2),400(3),300(5),400(5),800(2), use1:1300 val:3100
200(2),400(3),300(5),800(2),400(5), use1:1700 val:3100
200(2),400(3),400(5),300(5),800(2), use1:1000 val:3600
200(2),400(3),800(2),400(5),300(5), use1:1400 val:1600
200(2),800(2),300(5),400(3),400(5), use1:1000 val:2000
3900
才5个枚举的数据就很多了,这里已经省略了
没二维数组直观
参考代码
还有优化的空间
#include <stdio.h>
#include <stdlib.h>
#define MAX(a,b) (a)>(b)?(a):(b)
#define VAL_MODE 1
int solution(int N, int m, int arr[][2]){
int result =0;
int max=N;
//初始化二维数组
int **table=(int**)malloc(m*sizeof(int*));
for(int i=0;i<m;i++){
table[i]=(int*)malloc((max+1)*sizeof(int));
}
//特殊处理第一行
do{
//第一件物品的占用空间或金额
int use=arr[0][0];
//第一件物品对应价值,跟题目内容计算
#if VAL_MODE==1
int value=arr[0][0]*arr[0][1];
#else
int value=arr[0][1];
#endif
for(int j=0;j<=max;j++){
//空间不够直接0
if(j<use){
table[0][j]=0;
}else{
//空间够了就是本物品的价值
table[0][j]=value;
}
}
}while(0);
for(int i=1;i<m;i++){
int use=arr[i][0];
#if VAL_MODE==1
int value=arr[i][0]*arr[i][1];
#else
int value=arr[i][1];
#endif
for(int j=0;j<=max;j++){
if(j<use){
//空间不够直接用上一行的数据
table[i][j]=table[i-1][j];
}else{
//table[i-1][j] 不放该物品的价值
//value+table[i-1][j-use] 放了该物品+剩余空间的价值
table[i][j]=MAX(table[i-1][j],value+table[i-1][j-use]);
}
}
}
//返回最后一个数
result=table[m-1][max];
for(int i=0;i<m;i++){
free(table[i]);
}
free(table);
return result;
}
int swaparrpos(int arr[][2],int pos1,int pos2){
if(pos1==pos2){
return 0;
}
int use=arr[pos1][0];
int val=arr[pos1][1];
arr[pos1][0]=arr[pos2][0];
arr[pos1][1]=arr[pos2][1];
arr[pos2][0]=use;
arr[pos2][1]=val;
return 0;
}
//用个全局省事
int g_result=0;
//获取当前序列的价值
//CountN 当前数组要占用多少空间
int getarrvalue(int N, int m,int arr[][2],int *CountN){
int result=0;
int use=0;
for(int i=0;i<m;i++){
//空间或使用额度超了就提前结束
*CountN=*CountN+arr[i][0];
if((use+arr[i][0])>N){
break;
}
#if VAL_MODE==1
int value=arr[i][0]*arr[i][1];
#else
int value=arr[i][1];
#endif
use+=arr[i][0];
result+=value;
}
if(result>g_result){
g_result=result;
}
return result;
}
//递归排序
int solution3(int N, int m, int arr[][2],int pos){
if(pos==m){
int usem=0;
int ret=getarrvalue(N,m,arr,&usem);
for(int i=0;i<m;i++){
printf("%d(%d),",arr[i][0],arr[i][1]);
}
printf(" use2:%d val:%d\n",usem,ret);
return 0;
}
for(int i=pos;i<m;i++){
swaparrpos(arr,pos,i);
int CountN=0;
int ret=getarrvalue(N,pos+1,arr,&CountN);
//还能放继续继续遍历
if(CountN<N){
solution3(N,m,arr,pos+1);
}else{
for(int i=0;i<m;i++){
printf("%d(%d),",arr[i][0],arr[i][1]);
}
printf(" use1:%d val:%d\n",CountN,ret);
}
swaparrpos(arr,pos,i);
}
return 0;
}
int solution2(int N, int m, int arr[][2]){
int result=0;
solution3(N,m,arr,0);
result=g_result;
return result;
}
int main() {
#if 1
int N=1000;//总钱数
int m=5;//物品数
//价格及满意度
int arr[5][2]={
{800,2},
{400,5},
{300,5},
{400,3},
{200,2}
};
#else
int N=4;//总钱数
int m=3;//物品数
//价格及满意度
int arr[3][2]={
{1,1500},
{3,2000},
{4,3000}
};
#endif
int result = solution2(N, m, arr);
printf("%d", result);
return 0;
}
补充
如果空间足够,能收下所有的,还要这么折腾吗??
那就应该是逆向思维,从总数中拿走几个