http://poj.org/gotoproblem?pid=1742

(1)多重背包的处理方式有两种:

      1)转化为分组背包。本题用到了将硬币数转化成“1,2,4,8,... ,余数”的形式。

             2)转化为01背包。再开一个数组,记录每一个dp[i] 对每一种硬币的使用情况,以确保不多用硬币。

(2)以运行效率而言,上一条中2)的效率高一些(我的代码前者超时,后者AC,具体原因不是特别明确)。似乎本题就是在卡时间,在时间细节设陷阱,各位请注意。

  以下代码用的是转化为01背包。

 具体代码:

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int Inf = 1<<29;
int n, m;
int p[110], num[110];
int dp[100100], used[100100];
int main()
{
    int i, j, k;
    while(scanf("%d%d", &n, &m)!=EOF, n||m)
    {
        for(i=1;i<=n;i++) scanf("%d", &p[i]);
        for(i=1;i<=n;i++) scanf("%d", &num[i]);
        memset(dp, 0, sizeof(dp));
        dp[0]=1;
        int sum=0;
        for(i=1;i<=n;i++)
        {
            memset(used, 0, sizeof(used));
            for(j=p[i];j<=m;j++)
                if(!dp[j]&&dp[j-p[i]]&&used[j-p[i]]+1<=num[i])
                    dp[j]=1, used[j]=used[j-p[i]]+1, sum++;
        }
        printf("%d\n", sum);
    }
    return 0;
}


超时代码,用的是转化为分组背包:

View Code
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int Inf = 1<<29;
int n, m;
int p[101], num[101], map[101][11];
bool dp[100001];
int main()
{
    int i, j, k;
    while(scanf("%d%d", &n, &m)!=EOF, n||m)
    {
        for(i=1;i<=n;i++) scanf("%d", &p[i]);
        memset(map, 0, sizeof(map));
        for(i=1;i<=n;i++)   //  转化为分组背包
        {
            scanf("%d", &num[i]);
            int x=1, tot=0, tm=num[i];
            while(tm>0)
            {
                if(tm<x) {map[i][tot++]=tm;break;}
                map[i][tot++]=x;
                tm-=x; x*=2;
            }
        }
        fill(dp, dp+100001, 0);
        dp[0]=1;
        for(i=1;i<=n;i++)
            for(j=0;map[i][j];j++)
                for(k=m;k>=map[i][j]*p[i];k--)
                    dp[k]=dp[k]||dp[k-map[i][j]*p[i]];
        int sum=0;
        for(i=1;i<=m;i++)
            if(dp[i]) sum++;
        printf("%d\n", sum);
    }
    return 0;
}