完全背包(SOJ 3300)

SOJ 3300: Stockholm Coins http://acm.scu.edu.cn/soj/problem.action?id=3300

题意:给出n个正整数num[i], 0≤i<n,求解一个最小的数,满足要通过求和得到该数必须用上所有的num[i](每个num[i]可以用任意次)。若存在这样的数则输出,否则输出-1. 

分析:定义这n个数之和为sum=Σnum[i]. 不难验证,若该数存在,则一定就是sum。如果不存在,说明某些数可以被另外一些数代替,sum可以由多种表达方式。因此,若要知道该数是否存在,只需判断sum可由num[i], 0≤i<n, 的分解方式是否唯一。

例子1:3个数为:3,4,19,sum=3+4+19=26.

(1) 26=3+4+19

(2) 26=2*3+5*4

(2)说明19是多余的(19=5*3+4),任意一个数在分解需要19的时候都可以用3和4代替。

例子2:3个数为5,7,11,sum=5+7+11=23.

23=5+7+11,只有这一种分解方式。

基于上述分析,由于num[i]可以使用任意多次,这道题是一个完全背包问题。定义dp[i][j]为数j可由前i个数分解的方式数目,则dp[i][j]=dp[i-1][j]+dp[i][j-num[i]]. 初始dp[0]=1. 代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int coins[34];
int dp[320002];
int main()
{
    int T, N;
    scanf("%d", &T);
    int i, j;
    int sum;
    while (T--)
    {
        scanf("%d", &N);
        sum = 0;
        for (i = 0; i < N; i++)
        {
            scanf("%d", &coins[i]);
            sum += coins[i];
        }
        memset(dp, 0, sizeof(dp));
        dp[0] = 1;
        for (i = 0; i < N; i++)
            for (j = coins[i]; j <= sum; j++)
                dp[j] += dp[j-coins[i]];
        if (dp[sum] == 1)
            printf("%d\n", sum);
        else
            printf("-1\n");
    }
    return 0;
}
View Code

posted on 2019-03-06 18:01  小叶子曰  阅读(113)  评论(0编辑  收藏  举报

导航