完全背包”最优方案总数分析及实现 结合实际例题

Boys, get rid of singlehood

本文为网上复制

 

本人博文 《背包问题——“完全背包”详解及实现(包含背包具体物品的求解)》中已详细谈过完全背包问题,同时在博文 《背包问题——“01背包”最优方案总数分析及实现》中也总结过01背包的最优方案总数的实现。这里我们模仿01背包最优方案总数方法给出完全背包的最优方案求解方法。

   

        重写完全背包的动态规划的状态及状态方程:

        完全背包是在N物品中选取若干件(同一种物品可多次选取)放在空间为V的背包里,每物品的体积为C1,C2,…,Cn,与之相对应的价值为W1,W2,…,Wn.求解怎么装物品可使背包里物品总价值最大。

   

        设物品种类为N,背包容量为V,每种物品的体积为C[i],价值为W[i]。

子问题定义:F[i][j]表示前i物品中选取若干件物品放入剩余空间为j的背包中所能得到的最大价值。

       状态方程为:

                                     (2-2)

 

         在文章《背包问题——“01背包”最优方案总数分析及实现》中曾定义G[i][j]代表F[i][j]的方案总数。这里我们也做相同的定义,那么最终的结果应该为G[N][V]。

   

        由01背包转变到完全背包后G[i][j]该怎么求?

        对于01背包来说,G[i][j]求法如下(摘自:《背包问题——“01背包”最优方案总数分析及实现》):

        如果F[i][j]=F[i-1][j]且F[i][j]!=F[i-1][j-C[i]]+W[i]说明在状态[i][j]时只有前i-1件物品的放入才会使价值最大,所以第i件物品不放入,那么到状态[i][j]的方案数应该等于[i-1][j]状态的方案数即G[i][j]=G[i-1][j]

        如果F[i][j]=F[i-1][j-C[i]]+W[i] 且F[i][j]!=F[i-1][j]说明在状态[i][j]时只有第i件物品的加入才会使总价值最大,那么方案数应该等于[i-1][j-C[i]]的方案数,即G[i][j]=G[i-1][j-C[i]]

        如果F[i][j]=F[i-1][j-C[i]]+W[i] 且F[i][j]=F[i-1][j]则说明即可以通过状态[i-1][j]在不加入第i件物品情况下到达状态[i][j],又可以通过状态[i-1][j-C[i]]在加入第i件物品的情况下到达状态[i][j],并且这两种情况都使得价值最大且这两种情况是互斥的,所以方案总数为G[i][j]=G[i-1][j-C[i]]+ G[i-1][j]

 

        对于完全背包,我们也可以根据其状态方程来进行条件判断:

        如果F[i][j] = F[i-1][j]且F[i][j] != F[i][j-C[i]]+W[i],说明背包总不存在第i种物品,也就是说背包种物品仍由前i-1种物品组成,那么应该有G[i][j] = G[i-1][j]

        如果F[i][j] = F[i][j-C[i]]+W[i]且F[i][j] != F[i-1][j],则说明背包中必定存在第i种物品使背包达到[i][j]状态的最大值,G[i][j] = G[i][j-C[i]]

        如果F[i][j] = F[i][j-C[i]]+W[i]且F[i][j] = F[i-1][j],则说明背包中存在i与不存在i都可以达到最大值,那么这个方案数应该由两者叠加,因为这两种情况是互斥的,即G[i][j] = G[i-1][j]+G[i][j-C[i]]

        伪代码如下(注意和01背包情况的区分):

  1. F[0][] ← 0  
  2.   
  3. F[][0] ← 0  
  4.   
  5. G[][ ] ← 1  
  6.   
  7. for i ← 1 to N  
  8.   
  9.     do for j ← 1 to V  
  10.   
  11.         F[i][j] ← F[i-1][j]  
  12.   
  13.         G[i][j] ← G[i-1][j]  
  14.   
  15.         if (j >= C[i])  
  16.   
  17.             if (F[i][j] < F[i][j-C[i]]+W[i])  
  18.   
  19.                 then F[i][j] ← F[i][j-C[i]]+W[i]  
  20.   
  21.                      G[i][j] ← G[i][j-C[i]]  
  22.   
  23.             else if (F[i][j] = F[i][j-C[i]]+W[i])  
  24.   
  25.                 then G[i][j] ← G[i-1][j]+G[i][j-C[i]]  
  26.   
  27. return F[N][V] and G[N][V]  
F[0][] ← 0 F[][0] ← 0 G[][ ] ← 1 for i ← 1 to N do for j ← 1 to V F[i][j] ← F[i-1][j] G[i][j] ← G[i-1][j] if (j >= C[i]) if (F[i][j] < F[i][j-C[i]]+W[i]) then F[i][j] ← F[i][j-C[i]]+W[i] G[i][j] ← G[i][j-C[i]] else if (F[i][j] = F[i][j-C[i]]+W[i]) then G[i][j] ← G[i-1][j]+G[i][j-C[i]] return F[N][V] and G[N][V]

同样,上述方法在保存状态F[][]及G[][]时需要O(NV)的空间复杂度,下面我们对空间复制度进行优化。

 

压缩空间复杂度为O(V)

F[i][j]与G[i][j]只分别与F[i-1][]和G[i-1][]的状态有关,所以我们可以用两个一维数组F[]和G[]来替换二维数组F[][]和G[][]。

直接给出伪代码:

  1. F[] ← 0  
  2.   
  3. G[] ← 1  
  4.   
  5. for i ← 1 to N  
  6.   
  7.     do for j ← C[i] to V  
  8.   
  9.         if (F[j] < F[j-C[i]]+W[i])  
  10.   
  11.             then F[j] ← F[j-C[i]]+W[i]  
  12.   
  13.                  G[j] ← G[j-C[i]]  
  14.   
  15.         else if (F[j] = F[j-C[i]]+W[i])  
  16.   
  17.             then G[j] ← G[j]+G[j-C[i]]  
  18.   
  19. return F[V] and G[V]  
F[] ← 0 G[] ← 1 for i ← 1 to N do for j ← C[i] to V if (F[j] < F[j-C[i]]+W[i]) then F[j] ← F[j-C[i]]+W[i] G[j] ← G[j-C[i]] else if (F[j] = F[j-C[i]]+W[i]) then G[j] ← G[j]+G[j-C[i]] return F[V] and G[V]

和01背包的最优方案总数相比上述伪代码只是调换了一下C[i]与V的遍历顺序,具体思想请看博文《背包问题——“完全背包”详解及实现(包含背包具体物品的求解)》

 

例题:::

 

Boys, get rid of singlehood!

Time Limit : 3000/1000ms (Java/Other)   Memory Limit : 65535/32768K (Java/Other)
Total Submission(s) : 191   Accepted Submission(s) : 85
Font: Times New Roman | Verdana |Georgia
Font Size: ← →

Problem Description

Today is Single's Day. Tom hears there is an activity for single boys in FamilyMart. The rule
of this activity is that when the customer pays for the goods which he buys, if he can answer how
many different ways to pay the total money, he will be given a discount. But it is a hard problem
for Tom. Therefore, he asks you for help. Tom always thinks that you are clever enough to solve
this problem.
To simplify the problem, we assume there are only 3 nominal amounts of banknotes, 1 yuan,
2 yuan and 5 yuan.
Given an amount of N yuan, you are required to work out how many ways to pay with those
3 nominal amounts of banknotes mentioned above.

Input

The input file consists of several test cases.The first line of each test case contains an integer
N (1<= N <= 11111). Input is terminated by a value of zero (0) for n.

Output

Each test case prints one line made up of an integer which shows the number of the ways to pay.

Sample Input

5670

Sample Output

456

Author

羊仔
 
代码:
#include<stdio.h>
#include<string.h>
int val[11115];
int ge[11115];
int a[3]={1,2,5};
int n;
int  bag()
{
 int i,j;
 memset(val,0,(n+3)*sizeof(val[0]));
// memset(ge,0,(n+3)*sizeof(ge[0]));
 for(i=0;i<=n;i++)
  ge[i]=1;
 for(i=0;i<3;i++)
  for(j=0;j<=n;j++)
  {
   if(j>=a[i])
   {
    if(val[j]<val[j-a[i]]+a[i])
    {
     val[j]=val[j-a[i]]+a[i];
     ge[j]=ge[j-a[i]];
    }
    else if(val[j]==val[j-a[i]]+a[i])
    {
     val[j]=val[j];
     ge[j]=ge[j]+ge[j-a[i]];
    }
   }
  }
  return ge[n];
}
int main()
{
     int i,j;
   while(scanf("%d",&n))
   {
    if(n==0) return 0;
    else
    {
     bag();
     //for(i=0;i<=n;i++)
   //   printf("%d ",ge[i]);
     printf("%d\n",ge[n]);
    }
   }
   return 0;
}
 
此题主要练习背包
下面是非背包做法

一共有3种面值——1元、2元、5元,求有多少种组合。因为有1元的存在,不管2元和5元的怎么拼都能凑成恰好的钱数,所以可以不考虑1元的(只要2元和5元组合小于等于钱数即可);有几个2元的一共有(钱数/2+1(1是指不用两元的这种))种情况,所以现在讨论用几个5元的就可以了;5元的情况用一个for循环从0到钱数/5一扫就可以了。计数器计数。

代码如下:

#include<stdio.h>

main()

{

    int n,i,j,m,cnt;

    while(scanf("%d",&n)&&n!=0)

    {

        cnt=0;

        for(i=0;i<=n/5;i++)

        {

            m=n;

            cnt+=(m-5*i)/2+1;

        }

        printf("%d\n",cnt);

    }

    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值