Coins hdu-2844(多重背包+二进制优化)

Whuacmers use coins.They have coins of value A1,A2,A3...An Silverland dollar. One day Hibix opened purse and found there were some coins. He decided to buy a very nice watch in a nearby shop. He wanted to pay the exact price(without change) and he known the price would not more than m.But he didn't know the exact price of the watch.

You are to write a program which reads n,m,A1,A2,A3...An and C1,C2,C3...Cn corresponding to the number of Tony's coins of value A1,A2,A3...An then calculate how many prices(form 1 to m) Tony can pay use these coins.


 

Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0

Sample Output
8
4

思路:题目意思是给你n个硬币,m的值,求不超过m硬币有多少种表示方式。
首先我们要知道任何一个数都可以转化成(1,2,4,8,16,2的k次方的形式)。
比如 13=1+2+4+4+2;13=1+4+8;
为啥什么呢,网上有一堆公式证明,同学可以搜一下,我就不证了(我也不会证)
我们先列出二进制的1,2,4,8。。。
0001 1;
0010 2;
0100 4;
1000 8;
从中我们可以看到他的范围是0001-1111;
1111 是 15;
也就是说1-15用1,2,4,8就可以全部表达;
同理,扩展开来,任何一个数都可以用2的次方来表示。这就是二进制优化

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstring>
#include<stdio.h>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include <sstream>
#include<vector>
#include<cmath>    
#include<stack>
#include<time.h>
#include<ctime>
using namespace std;
#define io ios::sync_with_stdio(0),cin.tie(0)
#define ms(arr) memset(arr,0,sizeof(arr))
#define LD long double
#define LL long long
#define PI acos(-1.0)
#define INF 0x3f3f3f3f
#define inf 1<<30
#define maxn 1000009
#define  ll unsigned long long
struct node
{
    int price;
    int number;
}s[maxn];
int dp[maxn] = {};
int main()
{
    int n, m;
    while (~scanf("%d%d", &n, &m))
    {
        if (n == 0 && m == 0)break;
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &s[i].price);
        }
        for (int i = 1; i <= n; i++)
        {
            scanf("%d", &s[i].number);
        }
        int cnt;
        for (int i = 1; i <= n; i++)
        {
            cnt = s[i].number;
            for (int j = 1; j <= cnt; j <<= 1)//j*=2;
            {
                for (int k = m; k - j * s[i].price >= 0; k--)//用一维数组来存背包,注意要逆序,
                {
                    dp[k] = max(dp[k], dp[k - j * s[i].price] + j*s[i].price);
                }
                cnt -= j;//为啥要减呢,比方说一个数是17=1+4+8+4;我们如果不减的话就会运算1,2,4,8,16;很明显不对。
                //我们减去的话就会运行1+2+4+8=15,还剩一个2
            }
            if (cnt)//剩下的就在这里运行一遍
            {
                for (int k = m; k >= cnt * s[i].price; k--)
                {
                    dp[k] = max(dp[k], dp[k - cnt * s[i].price] + cnt * s[i].price);
                }
            }    
        }
        int sum = 0;
        for (int i = 1; i <= m; i++)
        {
            if (dp[i] == i)
            {
                sum++;
            }
        }
        printf("%d\n", sum);
    }
}

 

 

posted @ 2020-09-24 01:06  夜灯长明  阅读(129)  评论(0编辑  收藏  举报