zoukankan      html  css  js  c++  java
  • 多重背包的二进制优化

    做题链接

    多重背包的二进制优化

    输入格式

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

    接下来有 (N) 行,每行三个整数 (vi,wi,si),用空格隔开,分别表示第(i)种物品的体积、价值和数量。

    输出格式

    输出一个整数,表示最大价值。

    数据范围

    (0<N≤1000)
    (0<V≤2000)
    (0<vi,wi,si≤2000)

    提示:

    本题考查多重背包的二进制优化方法。

    输入样例

    4 5
    1 2 3
    2 4 1
    3 4 3
    4 5 2
    

    输出样例:

    10
    

    思路

    优化多重背包的优化

    首先,我们不能用完全背包的优化思路来优化这个问题,因为每组的物品的个数都不一样,是不能像之前一样推导来优化递推关系的。

    我们列举一下更新次序的内部关系:(温馨提示,记得将式子对齐后进行观看)

    (f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....))

    (f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+3*w , .....)) 由上两式,可得出如下递推关系

    (f[i][j]=max(f[i,j-v]+w , f[i-1][j]))

    本题的优化方式

    接下来,我介绍一个二进制优化的方法,假设有一组商品,一共有11个。我们知道,十进制数字 11 可以这样表示

    (11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B))

    正常背包的思路下,我们要求出含这组商品的最优解,我们要枚举12次(枚举装(0,1,2....11)个)。

    现在,如果我们把这11个商品分别打包成含商品个数为(1)个,(2)个,(4)个,(4)个(分别对应(0001,0010,0100,0100))的四个”新的商品 “, 将问

    题转化为(01)背包问题,对于每个商品,我们都只枚举一次,那么我们只需要枚举四次 ,就可以找出这含组商品的最优解。 这样就大大减

    少了枚举次数。

    这种优化对于大数尤其明显,例如有(1024)个商品,在正常情况下要枚举(1025)次 , 二进制思想下转化成(01)背包只需要枚举(10)次。

    优化的合理性的证明

    先讲结论:上面的(1,2,4,4)是可以通过组合来表示出0~11中任何一个数的,还是拿(11)证明一下(举例一下):

    首先,(11)可以这样分成两个二进制数的组合:

    (11=0111(B)+(11−0111(B))=0111(B)+0100(B))

    其中(0111)通过枚举这三个(1)的取或不取(也就是对(0001(B))(0010(B))(0100(B))的组合),可以表示十进制数0~7( 刚好对应了$ 1,2,4$ 可

    以组合出 (0~7) ) , (0~7)的枚举再组合上(0100(B))( 即 十进制的 4 ) ,可以表示十进制数 0~11。其它情况也可以这样证明。这也是为什么,这

    个完全背包问题可以等效转化为(01)背包问题,有木有觉得很奇妙

    怎么合理划分一个十进制数?

    上面我把(11)划分为

    (11=0111(B)+(11−0111(B))=0111(B)+0100(B))

    是因为 0111(B)刚好是小于11的最大的尾部全为1的二进制 ( 按照上面的证明,这样的划分没毛病 ) , 然后那个尾部全为1的数又可以 分解

    (0000....1 , 0000....10 , 0000....100) 等等。

    总结一下敲黑板

    就是先将所有的物品读入进来,然后将每个物品的(s)通过二进制,划分成多个进行打包,形成新的物品(价值和体积也要随之变化),每

    个原来的物品都这样进行处理最后再对打包后的物品做一遍01背包即可

    代码

    #include<bits/stdc++.h>
    using namespace std;
    struct node{
    	int v,w;
    };
    
    vector<node> G;
    const int N=20000;
    int f[N];
    int n,m;
    int main()
    {
    	cin>>n>>m;
    	int v,w,s;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>v>>w>>s;
    		for(int k=1;k<=s;k*=2)
    		{
    			G.push_back({k*v,k*w});
    			s-=k;
    		}
    		if(s>0) G.push_back({s*v,s*w});
    	}
    	for(int i=0;i<G.size();i++)
    	{
    		for(int j=m;j>=G[i].v;j--)
    		{
    			f[j]=max(f[j],f[j-G[i].v]+G[i].w);
    		}
    	}
    	cout<<f[m]<<endl;
    	return 0;
    }
    
  • 相关阅读:
    MySQL -- select count(1) 计算一共有多百少符合条件的行
    Python3 -- 文件I/O总结(with、read、write、txt、CSV等)
    Linux -- wget 之 FTP篇
    Linux -- head/tail 查看文件的指定行数
    linux -- 查看linux磁盘容量和文件夹所占磁盘容量
    Linux -- 查询某个文件夹下的文件数量
    Python3 -- 查看python安装路径以及pip安装的包列表及路径
    Python3 --Linux 编码注释# -*- coding:utf-8 -*-
    VisualStudio2013 如何打开之前版本开发的(.vdproj )安装项目
    const int *p与int *const p的区别(转:csdn,suer0101)
  • 原文地址:https://www.cnblogs.com/bangdexuanyuan/p/13931441.html
Copyright © 2011-2022 走看看