完全背包(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. 代码如下:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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; }