zoukankan      html  css  js  c++  java
  • POJ 3093 Margaritas on the River Walk

    题目大意:给定一个容量为V(题目里是D)的背包和N(题目里是V)件物品各自的体积,求有多少种方法使得背包再也装不下任何物品。

    思路:既然要使背包装不下任何物品,也就是说这时剩下的物品中体积最小的那一件也不能装入背包,所以就可以枚举剩下的最小体积的这件物品来进行动态规划了。

    定义dp[i]=装满体积为i的背包的方法数,volumn[i]=第i件物品的体积,sum[i]=前i件物品的体积之和。

    现在要枚举剩下的物品中体积最小的那一件了。先将所有物品按体积大小排序。

    首先明确,当第i件物品作为剩下的体积最小的物品时,前i-1件物品就都已经被放到背包里面去了。这时,背包剩余体积为V-sum[i-1];由于不能让第i件物品放入背包中,这时需要的是把体积为V-sum[i-1]-volume[i]+1 ~ V-sum[i-1]放满的方法数,(当然V-sum[i-1]-volume[i]+1可能小于0,要处理一下,k = max(V-sum[i-1]-volume[i]+1,0).)也就是dp数组所对应的那几个数字的和。

    如果从体积小的物品开始枚举,考虑当第i件物品不能放入背包的情况,此时,前i-1件物品就都已经被放到背包里面去了,那么就需要计算第i+1 ~ n件物品占据体积k ~ V-sum[i-1]的方法数,然后再在总方法数上加上dp数组对应的值。那么,第i件物品就被考虑了i-1次,此时的算法复杂度为O(N^2 * V)。

    为了使得每件物品只被放入到背包一次,考虑从体积大的物品开始枚举。当第i件物品不能放入背包中,而前i件物品都放入了背包中,这时,我们把已知的i+1 ~ N件物品占据体积k ~ V-sum[i-1]的方法数加到总的方法数ans上,然后再去取第i件物品做01背包,供考虑下一件物品不能放入背包的情况使用,直到枚举完全部的物品。

    如果你要问我,使用同样的方法来顺序枚举可以不可以,那这个很容易否定。因为在逆序枚举的时候,当第i件物品放不下的时候,第i件物品后的物品都被考虑过了,且第i件物品之后的物品也肯定放不下。而顺序枚举的话,第i件物品之前的物品都被考虑过作为当前放不下的最小物品了,第i件物品放不下不意味着前面被考虑过的i-1件物品放不下,这就违背我们当初的假设了,如果这么做了,还有装进背包的物品的方法就也被考虑进去了。

    下面贴一下用降序(逆序枚举)做的代码:

    View Code
     1 #include <iostream>
     2 #include <cstring>
     3 #include <cstdio>
     4 #include <algorithm>
     5 using namespace std;
     6 
     7 int dp[1010],volume[35],sum[35];
     8 
     9 inline int max(int a,int b)
    10 {
    11     return a > b ? a : b;
    12 }
    13 
    14 int main()
    15 {
    16     int ncase,N,V,num;
    17     scanf("%d",&ncase);
    18     for(num = 1;num <= ncase;num++)
    19     {
    20         scanf("%d%d",&N,&V);
    21         memset(sum,0,sizeof(sum));
    22         memset(dp,0,sizeof(dp));
    23 
    24         dp[0] = 1;
    25 
    26         int i,j,ans = 0;
    27         for(i = 1;i <= N;i++)
    28             scanf("%d",&volume[i]);
    29 
    30         sort(volume + 1,volume + 1 + N);
    31 
    32         for(i = 1;i <= N;i++)
    33             sum[i] = sum[i - 1] + volume[i];
    34 
    35         for(i = N;i >= 1;i--)
    36         {
    37             int k = max(0,V - sum[i - 1] - volume[i] + 1);
    38 
    39             for(j = V - sum[i - 1];j >= k;j--)
    40                 ans += dp[j];
    41 
    42             for(j = V;j >= volume[i];j--)
    43                 dp[j] += dp[j - volume[i]];
    44         }
    45 
    46         if(volume[1] > V)    ans = 0;
    47 
    48         printf("%d %d\n",num,ans);
    49     }
    50     return 0;
    51 }
  • 相关阅读:
    [导入]微软轻量级“代码生成器”—Repository Factory使用(上)
    数据结构练习(41)数组中三个只出现一次的数字
    数据结构练习(43)字符串的组合
    数据结构练习(37)复杂链表的复制
    数据结构练习(36)二叉树两结点的最低共同父结点
    数据结构练习(34)对称子字符串的最大长度
    数据结构练习(38)树的子结构
    数据结构练习(39)二叉树前中后序遍历的非递归实现
    数据结构练习(42)八皇后问题
    数据结构练习(35)数组中出现次数超过一半的数字
  • 原文地址:https://www.cnblogs.com/zhexipinnong/p/2772498.html
Copyright © 2011-2022 走看看