多重背包问题

来源:互联网 发布:如何删除mac上的照片 编辑:程序博客网 时间:2024/05/01 10:56

题目:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=2156

题意:charlie有四种硬币,价值分别为1,5,10,,25,给定每种价值硬币的个数,求出能够刚好买下coffee的硬币的个数,硬币应该尽可能的多,并且选出的硬币的价值之和要正好等于coffee的价值。

类型:多重背包问题

思路:1)在本题中最大的难点在于怎么记录每种硬币的个数。方法是构造一个数组pre,pre[j]=j-b[i],将pre初始化为0,然后将pre[0]初始化为-1。

            2)用一个used数组表示该种硬币的使用情况,used[j]=used[j-b[i]]+1;当used大于该种硬币的数目时,那么就要开始不挑选本种硬币,开始下一层循环。

            3)定义dp数组表示i元最多用多少枚硬币,同时也表示i元能否用已有的硬币表示,真为1,假为0,故首先将dp置为0,然后将dp[0]置为1。

            4)约束条件为if((!dp[j]||dp[j-b[i]]+1>dp[j])&&dp[j-b[i]]&&used[j-b[i]]+1<=a[i]),表示dp[j]为求解,或者dp[j]不够大,并且dp[j-b[i]]不为0(能够用已有的硬币表示)同时,该                   种硬币还足够,那么才能进入循环,进行更新。

#include<cstdio>

#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
#define N 10010
using namespace std;
int n,m;
int a[5],b[5]={0,1,5,10,25};
int dp[N];
int used[N];
int pre[N];
int ans[30];
int main()
{
    int i,j,k;
while(scanf("%d",&m)!=EOF)

    {

int count=m;

        for(i=1;i<=4;i++)

   { 

  scanf("%d",&a[i]);

  count+=a[i];

  }          

 if(!count)  break;
        memset(dp, 0, sizeof(dp));
        memset(pre, 0, sizeof(pre));
        dp[0]=1;
        pre[0]=-1;
        for(i=1;i<=4;i++)
        {
            for(j=0;j<=m;j++)
                used[j]=0;
            for(j=b[i];j<=m;j++)
            {
                if((!dp[j]||dp[j-b[i]]+1>dp[j])&&dp[j-b[i]]&&used[j-b[i]]+1<=a[i])
                {
                    dp[j]=dp[j-b[i]]+1;
                    used[j]=used[j-b[i]]+1;
                    pre[j]=j-b[i];
                }
            }
        }
        memset(ans,0,sizeof(ans));
      if(dp[m])
        {
            for(i=m;pre[i]!=-1;i=pre[i])
                ans[i-pre[i]]++;
            printf("Throw in %d cents, %d nickels, %d dimes, and %d quarters.\n", ans[1], ans[5], ans[10], ans[25]);
        }
        else printf("Charlie cannot buy coffee.\n");
    }
    return 0;
}
0 0
原创粉丝点击