hdu 1059 Dividing(二进制转化优化) 分组背包

多重背包问题
题目
N种物品和一个容量为V的背包。第i种物品最多有n[i]件可用,每件费用是c[i],价值是w[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
基本算法
转化为01背包问题
一种好想好写的基本方法是转化为01背包求解:把第i种物品换成n[i]01背包中的物品,

假如 一种物品的体积为数量为5  我们可以把其存在一个数组里面存5次  然后进行01背包中的操作

则得到了物品数为∑n[i]01背包问题,直接求解,复杂度仍然是O(V*∑n[i])
但是当数据很大的时候这种方法可能会超时  我们可以用状态压缩
可以考虑二进制的思想
方法是:将第i种物品分成若干件物品,其中每件物品有一个系数,这件物品的费用和价值均是原来的费用和价值乘以这个系数。使这些系数分别为 1,2,4,...,2^(k-1),n[i]-2^k+1,且k是满足n[i]-2^k+1>0的最大整数。例如,如果n[i]13,就将这种物品分成系数分别为1,2,4,6的四件物品。分成的这几件物品的系数和为n[i],表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示

例如    3可以用1+2 表示 也就是说 选择12对应的背包相当于选择系数为3对应的背包  这样就可以弥补系数为3的背包不存在的情况 

像假如一种物品的体积为数量为5  我们可以把他分为 1 2 2  而不是1 1 1 1 1  少了2个   效率就高了   背包少了2个    

这样就将第i种物品分成了O(log n[i])种物品,将原问题转化为了复杂度为O(V*∑log n[i])01背包问题,是很大的改进。

 看明白了吧 不明白的话要回去看看01和完全背包了   下面给个例子:
hdu 1059 Dividing(二进制转化优化)

Dividing

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5887    Accepted Submission(s): 1594

 Problem Description

 Marsha and Bill own a collection of marbles. They want to split the collection among themselves so that both receive an equal share of the marbles. This would be easy if all the marbles had the same value, because then they could just split the collection in half. But unfortunately, some of the marbles are larger, or more beautiful than others. So, Marsha and Bill start by assigning a value, a natural number between one and six, to each marble. Now they want to divide the marbles so that each of them gets the same total value.
Unfortunately, they realize that it might be impossible to divide the marbles in this way (even if the total value of all marbles is even). For example, if there are one marble of value 1, one of value 3 and two of value 4, then they cannot be split into sets of equal value. So, they ask you to write a program that checks whether there is a fair partition of the marbles.

 

 Input

Each line in the input describes one collection of marbles to be divided. The lines consist of six non-negative integers n1, n2, ..., n6, where ni is the number of marbles of value i. So, the example from above would be described by the input-line ``1 0 1 2 0 0''. The maximum total number of marbles will be 20000.
The last line of the input file will be ``0 0 0 0 0 0''; do not process this line.

Output

For each colletcion, output ``Collection #k:'', where k is the number of the test case, and then either ``Can be divided.'' or ``Can't be divided.''.

Output a blank line after each test case.

 

 Sample Input

 1 0 1 2 0 0 

 1 0 0 0 1 1 

 0 0 0 0 0 0

 

 Sample Output

 Collection #1: Can't be divided.

 Collection #2: Can be divided.

 

        此题属于多重背包,数据很大,需要二进制优化,不然超时。优化后直接当做01背包来做,01背包是逆序!

 链接:http://acm.hdu.edu.cn/showproblem.php?pid=1059

 题目大意:
        给你6个石子,石子的价值分别从1->t;6,然后输入各个石子的数量。之后要你判断石子能否分成两堆,使两堆石子的价值一样。想了好久,最后觉得才发现可以用多重背包做。
解题思路:
首先这6种石子不是无数个的,而且石子有价值,可以假设石子的体积和价值一样,反正题目都木有要求体积嘛。
一开始先算出石子的总价值,然后判断总价值的奇偶性,如果是奇数,肯定分不了,偶数的话就多重背包下。以石子的总价值的一半作为背包。初始化为负无穷,如果最后h_tal是总价值的一半,dp[h_tal]如果有数量的话,那么就证明这个价值可以达到,意思就是可以取石子取出价值为总价值的一半,那么就满足了题意要求的平均分为一半。

即如果能装满一半的背包    那必然能剩下一半  即能够把总的val 分成两份 
 ok,总的思路就是这样了,思路相当的清晰啊,不过这样做下去,直接就TLE了,因为这那个个总价值最多是20000*6,这可是120000/2啊,  然后3for循环,最坏的复杂度就是6*120000/2*20000.不超时才怪。
  其实做个简单的处理就可以了,把各种石子的数量模10就行了,因为一种石子超过10,比如12,那么前面10个是肯定可以均分掉的,所以就 不考虑它了,直接考虑2就行了。。

 也可以二进制压缩  看下面压缩代码   如果看不懂  最下面有我的代码  并且有解释
 代码:

 Cpp代码  

#include <iostream>   

#include <stdio.h>   

#include <memory.h>   

using namespace std;   

  

const int N = 6;   

int V, total, sum;   

int bag[120005];   

int w[15], n[15], v[1005];   

10   

11 void _01_bag()   

12 {   

13     int i, j;   

14     memset(bag, 0, sizeof(bag));   

15     for(i = 0; i < total; i++)   

16     {   

17         for(j = sum; j >= v[i]; j--)   

18         {   

19             bag[j] = max(bag[j], bag[j-v[i]]+v[i]);//令其体积和价值相同

20         }   //别忘记了上面的j>=v[i]别忘记等号哈

21     }   

22 }   

23   

24 int main()   

25 {   

26     int i, temp, zz = 1;   

     while(scanf("%d %d %d %d %d %d", &n[0], &n[1], &n[2], &n[3], &n[4], &n[5]))   

27     {   

28         if(n[0] + n[1] + n[2] + n[3] + n[4] + n[5] == 0) break;   

29         printf("Collection #%d:\n", zz++);   

30         V = 0;   

31         for(i = 0; i < N; i++)   

32         {   

33             w[i] = i + 1;   

34             V += w[i]*n[i]; //求总和

35         }   

36         if(V%2 == 1)    //总和是奇数则不能平分   

37         {   

38             printf("Can't be divided.\n\n");   

39             continue;   

40         }   

41         sum = V/2; total = 0;   

42         for(i = 0; i < N; i++)  //二进制压缩为//——01背包   

43         {   

44             if(n[i] == 0) continue;   

45             temp = 1;   

46             while(n[i] > temp)   

47             {   

48                 v[total++] = temp*w[i]; //将新的值//赋给v[]   

49                 n[i] -= temp;   

50                 temp *= 2;   

51             }   

52             v[total++] = n[i]*w[i];   

53         }   

54         _01_bag();  //用新的v[]数组直接拿来01背包   

55         if(bag[sum] != sum)   

56             printf("Can't be divided.\n\n");   

57         else  

58             printf("Can be divided.\n\n");   

59     }   

60   

61     return 0;   

62 }  


 

-----------------------------------我的代码---------------------------------------

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

/*我们可以把总价值分出来一半  然后把物体的价值和体积看成一样 用体积为总价值一半的背包放这些物体   如果能够放满这个背包 说明有

  办法分出来一半放进背包里面  这样就分成了两份  
 
 */
 
#include<stdio.h>
 
#include<string.h>
 
 int b[10000];// 存的是二进制压缩后的各个物体的价值 

int c[120005];//这个要大啊  因为下面还有c[val]而val是总价值的一半所以很大

    int bag(int val,int n)//它是01背包

{
 
 int i,j;
 
 memset(c,0,sizeof(c));
 
 for(i=0;i<n;i++)//控制物体的个数 一个一个的解决 即加上这个物体后下面将列出所有的能装下的情况
  
  for(j=val;j>=b[i];j--)//在各个已装体积下  腾出b[i]的空间把b[i]放进去 注意是各个体积下
   
  { c[j]=c[j]>c[j-b[i]]+b[i]?c[j]:c[j-b[i]]+b[i];//比较一下是放b[i]好 还是不放好
  
  //printf("%d %d\n",c[val],val);}
  
  if(c[val]==val) return 1;
  
  else return 0;
  
  }
  
  int main()
   
  {
   
   int a[10],i,cnt=1,sum_v,val,temp,j;
   
   while(1)
    
   {
    
    scanf("%d %d %d %d %d %d",&a[0],&a[1],&a[2],&a[3],&a[4],&a[5]);
    
    sum_v=a[0]+a[1]*2+a[2]*3+a[3]*4+a[4]*5+a[5]*6;
    
    if(sum_v==0) return 0;
    
    printf("Collection #%d:\n",cnt++);
    
    if(sum_v%2==1) {printf("Can't be divided.\n\n"); continue;}
    
    val=sum_v/2;j=0;
    
    for(i=0;i<6;i++)
     
    {
     
     if(a[i]==0) continue; 
     
     temp=1;
     
     while(a[i]>temp)
      
     {
      
      b[j++]=temp*(i+1);
      
      a[i]=a[i]-temp;
      
      temp=temp*2;
      
     }
     
     b[j++]=a[i]*(i+1);//注意这里哈 千万不要写成temp 一开始就一个劲错这里
     
     
     
    }
    
    if(bag(val,j)) printf("Can be divided.\n\n");//如果能够分出一个能装下一半价值的背包
    
    else printf("Can't be divided.\n\n");//那么剩下的是另一半 正好分成了2份
    
   }

     return 0;
   
  }

大家复制下来看吧   不知道为什么老是我复制的被挡住


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Eclipse二进制文件是指由Eclipse集成开发环境生成的可执行文件。在Linux下,Eclipse生成的二进制文件通常是没有扩展名的,如上述引用所示。在使用Eclipse编译和运行项目时,如果项目名包含扩展名(例如.hdu.c),则Eclipse可能无法正确识别该文件为二进制可执行文件。 这可能导致在运行时出现找不到二进制文件的错误。解决办法有两种:一是避免使用带有扩展名的项目名,另一种是手动创建一个运行配置,将命令写死以确保正确识别为二进制文件。 需要注意的是,上述讨论中的EclipseParser库是一个用于验证和读取Eclipse二进制文件内容的DLL库,它是通过按照二进制格式编写的。这个库可以验证和读取Eclipse二进制文件中的EGRID、INIT、UNRST等内容。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [linux下eclipse c++运行不了提示找不到二进制文件的解决方法](https://blog.csdn.net/bjrxyz/article/details/8974483)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Eclipse油藏数值模拟软件的二进制文件格式解析](https://blog.csdn.net/slofslb/article/details/119176891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值