思路
这是 0/1 背包的变种。
设 dp[j] 表示凑出和 j 所需的最少元素个数。
-
初始化
dp[0] = 0,其他为无穷大。 -
对每个数
x,从后往前更新:dp[j] = min(dp[j], dp[j - x] + 1)
但 target 可能为负,且数组元素可能为负,因此不能直接用下标表示负数。
若 target 绝对值不大,可以加偏移量 OFFSET。
若 target 绝对值过大,则伪多项式算法不可行,应说明使用 meet-in-the-middle 或搜索剪枝。
这里给出偏移量版本。
代码
c
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXN 1005
#define MAXT 200005
#define INF 0x3f3f3f3f
int min(int a, int b) {
return a < b ? a : b;
}
int main() {
int n, target;
int nums[MAXN];
int dp[MAXT];
int offset = 100000;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%d", &nums[i]);
scanf("%d", &target);
int size = 200005;
for (int i = 0; i < size; i++) dp[i] = INF;
dp[offset] = 0;
for (int i = 0; i < n; i++) {
int x = nums[i];
if (x >= 0) {
for (int j = size - 1; j >= x; j--) {
if (dp[j - x] != INF)
dp[j] = min(dp[j], dp[j - x] + 1);
}
} else {
for (int j = 0; j < size + x; j++) {
if (dp[j - x] != INF)
dp[j] = min(dp[j], dp[j - x] + 1);
}
}
}
int idx = target + offset;
if (idx < 0 || idx >= size || dp[idx] == INF)
printf("-1\n");
else
printf("%d\n", dp[idx]);
return 0;
}