【多重背包】二进制转换/ 其实有区别的--不全懂,会不放心的

占坑。。。

https://blog.youkuaiyun.com/qinzhenhua100/article/details/40350219

啊首先二进制这个,比如7可以化成1  10 100  完美表示。。

6可以是1 10 11(1 2 3  注意是从小到大加  完美表示。。)

多重背包其实就是拆成一个一个去做,由于有些麻烦所有就用二进制代替了,反正组成起来都一样

哇,看了霜雪千年大大的文章(感谢我谷((())))

非常清楚!!!

这人将来一定适合去当老师!!1

https://www.cnblogs.com/acgoto/p/8523258.html

 

关于此题:

代码:

            //那么这个题特殊化到没有重量 并且个数已经先天给好了就是2的c次方减一  
            // 首先 一定都是2的次方减一的话 我们知道可以是直接   01  10  100 。。这种的(所以直接*2就可以了)
            // 其次  价值是v  那么就是2*v  4*v  8*v的组合都可以达到
            //w[i]里面就装的是新转化过之后的体积。
 

 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL mod=1e9+7;
int t,n,q,cnt,x,y,num,c[22],val[500],s;LL dp[10005];
int main(){
    for(int i=0;i<21;++i)c[i]=(1<<i)-1;
    while(~scanf("%d",&t)){
        while(t--){
            memset(dp,0,sizeof(dp));dp[0]=1;cnt=0;
            scanf("%d%d",&n,&q);
            for(int i=1;i<=n;++i){
                scanf("%d%d",&x,&y),num=c[y];
                for(int d=1;d<=num;num-=d,d<<=1)
                    val[cnt++]=x*d;
                if(num>0)val[cnt++]=x*num;
            }
            for(int i=0;i<cnt;++i){
                for(int j=10000;j>=val[i];--j)
                    dp[j]=(dp[j]+dp[j-val[i]])%mod;
            }
            while(q--){
                scanf("%d",&s);
                printf("%lld\n",dp[s]);
            }
        }
    }
    return 0;
}

关键代码(雾)

 for(int i=0;i<cnt;++i){
                for(int j=10000;j>=val[i];--j)
                    dp[j]=(dp[j]+dp[j-val[i]])%mod;
            }

 

 

另外,HDU1284  钱币-------------

-----------没有博客园账号,悄咪咪转一下,给出链接 https://www.cnblogs.com/acgoto/p/8523258.html

这个赛时看了,但是这位小老弟怎么回事写的这么简单……………………

以下为复制

Problem Description

在一个国家仅有1分,2分,3分硬币,将钱N兑换成硬币有很多种兑法。请你编程序计算出共有多少种兑法。

Input

每行只有一个正整数N,N小于32768。

Output

对应每个输入,输出兑换方法数。

Sample Input

2934

12553

Sample Output

718831

13137761

解题思路:这道题可以当做数学题来做。假设某种方案要使用i枚3分硬币(i∈[0,n/3]),那么剩下的就有n-3*i分需要用2分和1分补全。对于2分硬币的个数,可能使用0,1,·····(n-3*i)/2枚,剩下的全都用1分硬币即可。也就是说当使用i枚3分硬币时,就会产生出{(n-3*i)/2+1}*1=(n-3*i)/2+1种方案,那么只要枚举i,将所有方案数相加即可。

-----自己想了一下,的确是,可能使用1是一种,2是一种,……  一直到(。。x)个,大概是因为只有abc三个吧。。。还真爽-。-  只不过别忘记那个0  就是全是1的时候--------

AC代码一:

复制代码

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int sum,n;
 5     while(cin>>n){
 6         sum=0;
 7         for(int i=0;i*3<=n;++i)
 8             sum+=(n-i*3)/2+1;
 9         cout<<sum<<endl;
10     }
11     return 0;
12 }

复制代码

 AC代码二:考虑dp,dp[j]表示用若干个硬币组成钱j的方案数,易得状态转移方程为:dp[j]+=dp[j-i](j>=i),意思是当前币值是i,那么在组成钱j的基础上还可以这样增加新的方案数:用之前的j-i分再和当前i分组成钱j即增加了dp[j-i]*1这么多的方案数。举个栗子:现将3分钱兑换成硬币的所有方案数有①1+1+1=3--->1种;②去掉2枚1分换成1枚2分的硬币1+2=3,那么增加了之前的1种方案数,现共有2种方案数(dp[3]+=dp[3-2]);③还有一种就是用1枚3分的硬币替换3枚1分的硬币3+0=3,定义组成0钱的方案数为1种,那么此时也增加1种方案数(dp[3]+=dp[3-3]),所以组成3分钱共有3种方案数。注意:初始化dp数组全为0,定义dp[0]=1,因为组成钱0(事实上钱0是由钱i-i=0即i=i这种情况得来的)也算一种方案数,然后对于每种币值,从i~最大35000枚举更新累加对应组成钱j的方案数即可。

----------------------自己又想了一下,这个dp理论上说应该和顺序mei有关系啊。

----------------------即使好几个i是一样的,那也是多了一种方案,因为算作的是不同的物品。……

比如,倘若3元兑换,有1种。(1个3)此时dp[0]=1   dp[1]=dp[2]=0

然后1元,dp[1]=1  dp[2]=1  (自己的状态,加上没带i之前的状态/i是新的,即,虽然新来的服务员名字和原来的一样都被叫做了服务员1号但是他们相貌其实是不相同的。。呈现在纸上的只有那个名号。。 他们还是不同的呀)

dp[3]=dp[3]+dp[2]=2(一个是自己的,一个是全1组成的)

然后2 ,dp[1]----  dp[2]=dp[2]+dp[0] (用完了2和之前1组成的那个2)

dp[3]=dp[0]+dp[3]=3... 没有疑问了

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 int main(){
 4     int n,dp[35000]={1};
 5     for(int i=1;i<=3;++i)//币值
 6         for(int j=i;j<35000;++j)//钱j
 7             dp[j]+=dp[j-i];
 8     while(cin>>n){cout<<dp[n]<<endl;}
 9     return 0;
10 }

-------------------

dp这个也很简单,像背包一样,从小到大装,组成方式啊什么的

关键代码。。。

 for(int i=0;i<cnt;++i){
                for(int j=10000;j>=val[i];--j)
                    dp[j]=(dp[j]+dp[j-val[i]])%mod;
        }

 

还有。。。要注意。。。。。。它们是从0转化而来的,所以dp[0]=1 

见过很多次了。。题目里→_→

 

讲道理嘛,下面这样写也可也

https://blog.youkuaiyun.com/tianyizhicheng/article/details/82716691  

---------------------------

好了,恭喜我又突破一个境界,看懂了不够,你要搞懂每一个细节。。。那些执行力不够/意志力不够/在很多情况下体现出自卑的人,常常觉得取得了一定的成就就可以了(其实你不写根本 就是理解 的不深刻嘛)(咦谁给我搞成中文标点输入法了。。)恭喜我 继续解锁一个成就  在大人刷抖音小孩玩滑梯放着流行歌曲的kfc比在家效率高写了this。。。。。

   for(int i=1;i<=n;++i){
                scanf("%d%d",&x,&y),num=c[y];
                for(int d=1;d<=num;num-=d,d<<=1)
                    val[cnt++]=x*d;//其实可以不必这样,因为给的是2^x-1 肯定除尽。。  可以直接*2 当然这样子写最安全的没错了。。 记得下面这句↓
//原理呢就是分出1减去1分出2进去2 下一个该分4了啊不够了我糖糖一个6只有3分给你  不用怕 你把3拿走吧(就是下面这句剩余价值--虽然本题应该还是用不到)
                if(num>0)val[cnt++]=x*num;
            }
// 嘛。物品是这样的,从第一个开始跑i,我们知道;其次,背包要倒着写,最后大于val[x],因为不是完全背包,所以只能加一次
// val这里只要把V乘起来当做一起扩大就可以了。。
            for(int i=0;i<cnt;++i){
                for(int j=10000;j>=val[i];--j)
                    dp[j]=(dp[j]+dp[j-val[i]])%mod;
            }

 

<think>首先,用户要求使用中文回答问题。问题是关于“C++多重背包二进制拆分”。我需要解释多重背包问题以及如何使用二进制拆分来解决它。 多重背包问题:在背包问题中,每个物品有重量和价值,但多重背包允许每个物品有多个实例(即有限的数量)。目标是选择物品放入背包,使得总重量超过背包容量,同时总价值最大化。 二进制拆分是一种优化技术,用于将多重背包问题转化为0-1背包问题。通过将每个物品拆分成多个二进制表示的子物品,从而减少物品数量,提高效率。 步骤: 1. 对于每个物品i,有数量为c[i],价值为v[i],重量为w[i]。 2. 将c[i]拆分成二进制表示:例如,如果c[i] = 13,可以拆分为1, 2, 4, 6(因为1+2+4+6=13)。 3. 每个拆分后的子物品代表一个新的物品,其价值为v[i] * k,重量为w[i] * k,其中k是二进制的部分。 4. 然后,问题转化为0-1背包问题,其中每个新物品只能选或选。 在C++中实现: - 需要处理输入:物品数量、背包容量、每个物品的重量、价值和数量。 - 使用动态规划(DP)解决0-1背包问题。 - DP数组dp[j]表示容量为j时的最大价值。 我将提供一个简单的C++代码示例。 代码结构: - 输入处理。 - 二进制拆分:创建新的物品列表。 - 初始化DP数组。 - 遍历新物品,更新DP。 - 输出最大价值。 确保代码正确:使用适当的循环和更新。 现在,回答完问题后,必须提出3-5个相关问题。问题必须以“§§相关问题§§”开头,并在同一行输出,然后列出1. 2. 3. 等。 相关问题应该基于用户的问题和我的回答。可能的主题: 1. 二进制拆分的原理。 2. C++实现细节。 3. 优化技巧。 4. 与其他背包问题的比较。 5. 实际应用。 列出3-5个: 1. 二进制拆分为什么能减少时间复杂度? 2. 在C++中如何高效地实现二进制拆分? 3. 多重背包问题还有哪些其他解决方法? 4. 二进制拆分适用于哪些场景? 5. 如何处理背包容量很大的情况? 确保格式:第一行必须是“§§相关问题§§”,然后直接列出问题,用数字点,要换行分散。示例中是“1. ···”等,所以我也用点。 最终输出结构: 1. 回答问题(中文)。 2. 提供C++代码(用代码块)。 3. 然后“§§相关问题§§”后跟问题列表。</think>在C++中,多重背包问题(Multiple Knapsack Problem)是指每个物品有多个实例(即有限数量),目标是在超过背包容量的前提下最大化总价值。二进制拆分(Binary Splitting)是一种优化技术,将多重背包转化为0-1背包问题,通过将每个物品的数量拆分为二进制表示(如数量13拆为1、2、4、6),从而减少物品数量,降低时间复杂度至O(V*Σlog c_i),其中V是背包容量,c_i是物品数量。 ### 二进制拆分原理: 1. **拆分过程**:对于每个物品i(重量w_i、价值v_i、数量c_i),将其拆分为若干子物品。每个子物品的数量k是2的幂(如1,2,4,...),直到剩余数量足下一个幂(如6)。子物品的重量为w_i * k,价值为v_i * k。 2. **转化为0-1背包**:拆分后,每个子物品视为一个独立物品(只能选或选),然后用标准0-1背包的DP求解。 3. **DP状态转移**:设dp[j]为容量j时的最大价值。遍历所有子物品,逆向更新:dp[j] = max(dp[j], dp[j - weight] + value)。 ### C++实现示例 以下代码演示多重背包二进制拆分解法: ```cpp #include <iostream> #include <vector> #include <algorithm> using namespace std; int main() { int n, V; // n: 物品种类数, V: 背包容量 cin >> n >> V; vector<int> weights, values; // 存储拆分后的新物品 // 输入原始物品:重量w, 价值v, 数量c for (int i = 0; i < n; i++) { int w, v, c; cin >> w >> v >> c; // 二进制拆分 for (int k = 1; k <= c; k *= 2) { weights.push_back(w * k); values.push_back(v * k); c -= k; } if (c > 0) { // 处理剩余数量 weights.push_back(w * c); values.push_back(v * c); } } // 0-1背包DP:dp[j]表示容量j的最大价值 vector<int> dp(V + 1, 0); for (int i = 0; i < weights.size(); i++) { for (int j = V; j >= weights[i]; j--) { // 逆向遍历防止重复选择 dp[j] = max(dp[j], dp[j - weights[i]] + values[i]); } } cout << "最大价值: " << dp[V] << endl; return 0; } ``` **代码说明**: - 输入格式:先输入物品种类数n和背包容量V,然后每行输入一个物品的重量、价值和数量。 - 拆分过程:对每个物品,循环拆分出2的幂次子物品(如c=13时拆为k=1,2,4,6)。 - DP更新:使用一维数组dp,从大到小遍历容量,确保每个子物品只选一次。 - 时间复杂度:O(V * Σlog c_i),空间复杂度O(V)。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值