HDU 2844 Coins 多重背包

原题链接:点击此处

解题思路:

多重背包问题,不太理解,理解了再补充。

网上博客的思路是:

对于每个硬币而言:
 
IF 价值×数量>=m
 
       THEN 取这个硬币的次数相当于无限制,可以考虑成完全背包
 
ELSE
 
       THEN 考虑成0-1背包(二进制优化),就是把这个硬币的v和num组合出0-1背包可能出现的状态(可以去看背包九讲)
 
(对于num,类似于编码。  当2^n <=num/2时:k = 2^n(n=012,……)表示状态,对应下来就是二进制的某一位数是1,然后还有一个状态就是k>num/2的时候啦,num+1-k,这样下来就可以用k来组合枚举出从1->num的所有可能了。然后对于k,单位价值和大小都乘上k之后就变成了一个0-1背包)
View Code

源代码如下:

#include <stdio.h>  
#include <algorithm>  
#include <string.h>  
using namespace std;  
  
const int MAX=100000;  
int dp[MAX];  
int c[MAX],w[MAX];  
int v;  
  
void ZeroOnePack(int cost,int wei)//01  
{  
    int i;  
    for(i = v;i>=cost;i--)  
    {  
        dp[i] = max(dp[i],dp[i-cost]+wei);  
    }  
}  
  
void CompletePack(int cost,int wei)//完全  
{  
    int i;  
    for(i = cost;i<=v;i++)  
    {  
        dp[i] = max(dp[i],dp[i-cost]+wei);  
    }  
}  
  
void MultiplePack(int cost,int wei,int cnt)//多重  
{  
    if(v<=cnt*cost)
    {  
        CompletePack(cost,wei);  
        return ;  
    }  
    else
    {  
        int k = 1;  
        while(k<=cnt)  
        {  
            ZeroOnePack(k*cost,k*wei);  
            cnt = cnt-k;  
            k = 2*k;  
        }  
        ZeroOnePack(cnt*cost,cnt*wei);  
    }  
}  
  
int main()  
{  
    int n;  
    while(~scanf("%d%d",&n,&v),n+v)  
    {  
        int i;  
        for(i = 0;i<n;i++)  
        scanf("%d",&c[i]);  
        for(i = 0;i<n;i++)  
        scanf("%d",&w[i]);  
        memset(dp,0,sizeof(dp));  
        for(i = 0;i<n;i++)  
        {  
            MultiplePack(c[i],c[i],w[i]);  
        }  
        int sum = 0;  
        for(i = 1;i<=v;i++)  
        {  
            if(dp[i]==i)  
            {  
                sum++;  
            }  
        }  
        printf("%d
",sum);  
    }  
    return 0;  
}  
View Code
原文地址:https://www.cnblogs.com/gdvxfgv/p/5767516.html