算法学习之路3 背包算法和排列组合

序言

背包算法算是常见的算法题,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;
}

补充

如果空间足够,能收下所有的,还要这么折腾吗??
那就应该是逆向思维,从总数中拿走几个

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值