C语言 多重背包问题 考虑二进制优化

思路:就是将多重背包问题转化成01背包

比如 v,w,s是1 2 3 时,就可以将这3个物品简化成2个 v,w 分别是 1 2 和2 4 的物品

将所有的物品简化完之后,问题就变成了01背包问题(即所有物品就只能使用1次)

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAXN 1000
#define MAXV 2000

int max(int i, int j)
{
    return i>j?i:j;
}


int main()
{
    int N=0,V=0,i=0,j=0;

    scanf("%d %d",&N,&V);
    if(!N||!V||N>MAXN||V>MAXV) return 1;

    int v=0,w=0,s=0,pos=0;
    int **newList = (int **)malloc(sizeof(int **));
    memset(newList,0,sizeof(newList));

    for(i=1;i<=N;i++)
    {
        scanf("%d %d %d",&v, &w, &s);
        //二进制优化
        int t=1;
        //以 v=1,w=2,s=9 为例子,9=1+2+4 +2
        //所以 s=2时 while结束 这9个物品就被分成4个,但是他们加起来的总价值和总体积不变
        //分别是
        //1,2  
        //2,4
        //4,8
        //2,4
        while(s>t)
        {
            pos++;
            newList = (int **)realloc(newList,sizeof(int **)*(pos+1));    //动态分配内存,防止空间浪费
            newList[pos] = (int *)malloc(sizeof(int *)*2);
            newList[pos][0]=v*t;
            newList[pos][1]=w*t;
            s-=t;
            t*=2;
        }
        if(s>0)
        {
            pos++;
            newList = (int **)realloc(newList,sizeof(int **)*(pos+1));
            newList[pos] = (int *)malloc(sizeof(int *)*2);
            newList[pos][0]=s*v;
            newList[pos][1]=s*w;
        }
        
    }
    
    int wList[V+1];
    
    memset(wList,0,sizeof(wList));
    //01背包问题的解法
    for(i=1;i<=pos;i++)
    {
        for(j=V;j>=newList[i][0];j--)
        {
            wList[j]=max(wList[j],wList[j-newList[i][0]]+newList[i][1]);
        }
    }
    printf("%d",wList[V]);
    return 0;
}

节省空间的方法,一边读取数据,一边判断最优解;可以先理解上面的算法再来看下面的方法

#include "stdio.h"
#define MAXN 1000
#define MAXV 2000
#define max(x,y) x>y?x:y


int main()
{
    int N=0,V=0,i=0,j=0;
    scanf("%d %d",&N,&V);
    if(!N||!V||N>MAXN||V>MAXV) return 1;
    
    int v=0,w=0,s=0;
    int dp[V+1];
    memset(dp,0,sizeof(dp));
    for(i=1;i<=N;i++)
    {
        scanf("%d %d %d",&v, &w, &s);
        for(int k=1;k<=s;k*=2)
        {
            for(j=V;j>=k*v;j--)
            {
                dp[j]=max(dp[j],dp[j-v*k]+w*k);
            }
            s-=k;
        }
        if(s)
        {
            for(int j=V;j>=s*v;j--)
            {
                dp[j]=max(dp[j],dp[j-s*v]+s*w);
            }
        }
    }
    printf("%d",dp[V]);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值