Coins【暑期培训Z题】【多重背包】

        一道用来防AK的题,但是被我们给弄出来了,还是挺可以的。

People in Silverland use coins.They have coins of value A1,A2,A3...An Silverland dollar.One day Tony opened his money-box 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. 
Input
The input contains several test cases. The first line of each test case contains two integers n(1<=n<=100),m(m<=100000).The second line contains 2n integers, denoting A1,A2,A3...An,C1,C2,C3...Cn (1<=Ai<=100000,1<=Ci<=1000). The last test case is followed by two zeros.
Output
For each test case output the answer on a single line.
Sample Input
3 10
1 2 4 2 1 1
2 5
1 4 2 1
0 0
Sample Output
8
4

        这道题就是要我们求在一定范围内,能够用手头上的一定数量的钱构成几种类型的总和,譬如一个一块和一个两块,在三块钱范围内能组成{1}、{1+2}、{2}的组合。

        思路:这道题如果用普通的多重背包,例如这样(就会超时):

for(int i=0; i<n; i++)

{

for(int j=1; j<=coin[i].b; j++)

{

for(int k=m; k>=coin[i].a*j; k--)

{

if(dp[k-coin[i].a] && !dp[k])

{

dp[k]=1;

sum++;

}

}

}

}

既然都说过了会超时,当然也会讲不超时的算法和思路,

    上面我们用了3个for循环O(n^3)的算法当然的T,我们发现第三个for中的

int k=m; k>=coin[i].a*j; k--

会被一遍遍的遍历,那么我们换种方式,假如知道此时取了几次岂不是更好,那么用一个s数组来存储到达这一个值我们所用去的步数就可以了。

利用s[i]与dp[i]同步的方法,记录到达这个值时候的步数,我们仅需要在循环中假如条件:

s[k-coin[i].a]<coin[i].b

即可,但为什么取小于符是因为原式应当为s[k]<=coin[i].b,但由于s[k]为接下来的处理量,所以我们才对它的前一步进行处理。


完整代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m;
struct node
{
    int a,b;        //价值,数量
}coin[105];
int dp[100005];
int s[100005];
int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)break;
        memset(coin, 0, sizeof(coin));
        memset(dp, 0, sizeof(dp));
        dp[0]=1;
        int sum=0;
        for(int i=0; i<n; i++)
        {
            scanf("%d",&coin[i].a);
        }
        for(int j=0; j<n; j++)
        {
            scanf("%d",&coin[j].b);
            if(coin[j].a*coin[j].b>m)
            {
                coin[j].b=m/coin[j].a;
            }
        }
        for(int i=0; i<n; i++)
        {
            memset(s, 0, sizeof(s));
            for(int k=coin[i].a; k<=m; k++)
            {
                if(dp[k-coin[i].a] && !dp[k] && s[k-coin[i].a]<coin[i].b)
                {
                    dp[k]=1;
                    s[k]=s[k-coin[i].a]+1;
                    sum++;
                }
            }
        }
        printf("%d\n",sum);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wuliwuliii

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值