混合背包问题(java and c++)

一、题目

有 N种物品和一个容量是 V的背包。

物品一共有三类:

第一类物品只能用1次(01背包);
第二类物品可以用无限次(完全背包);
第三类物品最多只能用 si次(多重背包);
每种体积是 vi,价值是 wi。

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

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

接下来有 N行,每行三个整数 vi,wi,si,用空格隔开,分别表示第 i种物品的体积、价值和数量si=−1
 表示第 i种物品只能用1次;
si=0
 表示第 i种物品可以用无限次;
si>0表示第 i
 种物品可以使用 si次;
输出格式
输出一个整数,表示最大价值。

数据范围
0<N,V≤1000

0<vi,wi≤1000

−1≤si≤1000
输入样例
4 5
1 2 -1
2 4 1
3 4 0
4 5 2
输出样例:
8

二、思路

思路参考acwing大佬的代码

首先将多重背包、01背包、完全背包都转化为多重背包处理,进行二进制优化

二进制优化就是指,将n个物品分成1,2,4...个一组,并将每组的体积和价值算出,这样就可以将这组看成一个物品,如果到第2的i次幂个一组不够分了,就将剩的物品放在一组

s<0 表示只能分成1个物品一组;s==0表示无穷多个物品,可是只有m容量,就算都放这个物品最多也只能放m/a个此物品

最后就回到了01背包问题 进行一维数组优化

三、java and c++代码

java

import java.io.*;

public class Main {
    //定义数组存放新的分组(物品)体积 价值 在体积为i时物品价值f[i]
    static int[] v = new int[100010];
    static int[] w = new int[100010];
    static int[] f = new int[100010];

    public static void main(String[] args) throws Exception{
        StreamTokenizer re = new StreamTokenizer(new InputStreamReader(System.in));
        PrintWriter pt = new PrintWriter(System.out);
        //新的分组物品下标
        int cnt = 1;
        //一共n个物品 背包容量为m
        re.nextToken();int n = (int)re.nval;
        re.nextToken();int m = (int)re.nval;
        //循环输入每个物品的体积a 价值b s>0 表示有s个此物品 <0表示有1个该物品 ==0表示有无穷个该物品 无穷个的时候 最多能放s = m / a向下取整个物品
        for(int i = 1;i <= n;i ++)
        {
            re.nextToken();int a = (int)re.nval;
            re.nextToken();int b = (int)re.nval;
            re.nextToken();int s = (int)re.nval;
            //使用二进制优化 物品数量以2倍速增长 并更新物品体积数组 使得每个分组总体积成为一个新的物品
            int k = 1;
            if(s < 0) s = 1;//表示01背包
            else if(s == 0) s = m / a;//表示完全背包 最多只能放m/a个这个物品
            while(s >= k)
            {
                //说明还有物品足够分成k个一组
                v[cnt] = k * a;
                w[cnt] = k * b;
                s -= k;
                k *= 2;
                cnt ++;
            }
            if(s > 0)
            {
                //不够分k个一组但是还剩下物品 将剩下的物品分成一组
                v[cnt] = s * a;
                w[cnt] = s * b;
                s -= s;
                cnt ++;
            }

        }
        //此时变成01背包问题一维数组优化
        for(int i = 1;i <= cnt;i ++)
        {
            for(int j = m;j >= v[i];j --)
            {
                f[j] = Math.max(f[j],f[j - v[i]] + w[i]);
            }
        }
        pt.println(f[m]);
        pt.close();

    }
}

c++

#include <bits/stdc++.h>
using namespace std;
int n,m,v[100010],w[100010],f[100010];
int main()
{
	cin>>n>>m;
	int cnt = 1;
	for(int i = 1;i <= n;i ++)
	{
		int a,b,s;
		cin>>a>>b>>s;
		int k = 1;
		//表示第i件物品只能用一次 
		if(s < 0)
			s = 1;
		//表示第i件物品有无穷多个 最多只能放s个 
		else if(s == 0) s = m / a;
		while(k <= s)
		{
			v[cnt] = a * k;
			w[cnt] = b * k;
			s -= k;
			k *= 2;
			cnt ++;
			
		}
		if(s > 0)
		{
			v[cnt] = s * a;
			w[cnt] = s * b;
			cnt ++;
		}
	}
	//将多重背包进行二进制优化
	for(int i = 1;i <= cnt;i ++)
	{
		for(int j = m;j >= v[i];j --)
			f[j] = max(f[j],f[j - v[i]] + w[i]);		
	}
	cout<<f[m]<<endl;
	return 0;
}

四、总结

这两个代码逻辑相同,明显感受到c++语言输入输出的方便性,但是对于c++语言没有java语言熟练。刚开始看大佬思路一头雾水,用我的DEVC++打了两遍思路逐渐清晰,就转化为java代码,大佬的代码属实妙不可言,评论区全是666。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值