• POJ_1014 Dividing(多重背包问题)


      这题做了将近一个月,断断续续的看背包问题,今天总于一口气把背包九讲中的前三讲看完了。

    这个一个很典型的多重背包问题:第i件物品有n[i]种,所占的容量权值分别是c[i],w[i]。求最大容量为V时所得到的最大权值。按照背包九讲的讲解,可以利用二进制思想,把n[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的四件物品。(1.....n[i]中的任意一个数都能由若干个系数相加得到,保证了[1, n[i]]区间上任意一个数都是可达的)

    为什么这样正确呢?

    证明:

        (1) 数列1,2,4,…,2^(k-1),n-2^k+1中所有元素的和为n,所以若干元素的和的范围为:[1, n];

        (2)如果正整数t<= 2^k – 1,则t一定能用1,2,4,…,2^(k-1)中某几个数的和表示,这个很容易证明:我们把t的二进制表示写出来,很明显,t可以表示成 n=a0*2^0+a1*2^1+…+ak*2^(k-1),其中ak=0或者1,表示t的第ak位二进制数为0或者1.

        (3)如果t>=2^k,设s=n-2^k+1,则t-s<=2^k-1,因而t-s可以表示成1,2,4,…,2^(k-1)中某几个数的和的形式,进而t可以表示成1,2,4,…,2^(k-1),s中某几个数的和(加数中一定含有s)的形式。

        (证毕!)

      转成01背包问题之后,将总的marbles的一半看成V,二进制压缩后的序列W[i]即作为c[i],又作为w[i]。状态转移方程为:

    for i: 1 .. n

      
    for j: sum/2 .. w[i]

        dp[j]
    = max(dp[j], dp[j-w[i]] + w[i]);



    代码:

    #include <iostream>
    #include
    <cstdio>
    #include
    <cstring>

    using namespace std;
    const int N = 120007;
    int num[6];
    int w[N], dp[N];

    int main()
    {
    //freopen("data.in", "r", stdin);

    int cas = 0, i, j;
    while(scanf("%d%d%d%d%d%d", &num[0], &num[1], &num[2], &num[3], &num[4], &num[5]) != EOF)
    {
    if(!num[0] && !num[1] && !num[2] && !num[3] && !num[4] && !num[5])
    break;
    memset(w,
    0, sizeof(w));
    memset(dp,
    0, sizeof(dp));

    int sum = 0;
    for(i = 0; i < 6; i++)
    sum
    += (i+1)*num[i];

    printf(
    "Collection #%d:\n", ++cas);
    if(sum&1)
    {
    printf(
    "Can't be divided.\n\n");
    continue;
    }

    int k = 0; sum >>= 1;
    for(i = 0; i < 6; i++) //利用二进制思想压缩成0-1背包问题
    {
    if(!num[i]) continue;
    int t = 1;
    while(t < num[i])
    {
    w[k
    ++] = t*(i+1);
    num[i]
    -= t;
    t
    <<= 1;
    }
    w[k
    ++] = num[i]*(i+1);
    }

    for(i = 0; i < k; i++)
    for(j = sum; j >= w[i]; j--)
    dp[j]
    = max(dp[j], dp[j-w[i]] + w[i]); //0-1背包

    if(dp[sum] == sum) //总数的1/2可达。
    printf("Can be divided.\n\n");
    else
    printf(
    "Can't be divided.\n\n");
    }
    return 0;
    }
  • 相关阅读:
    Mybatis基础配置及增删查改操作
    SpringMVC注解方式与文件上传
    SpringMVC的基础配置及视图定位
    Spring AOP面向切面编程
    Spring注入属性、对象
    Spring的配置及jar包下载
    多线程
    集合框架
    I/O————流
    I/O————对象流
  • 原文地址:https://www.cnblogs.com/vongang/p/2160665.html
Copyright © 2020-2023  润新知