题目
:给定一个非负整数序列 x1, x2, …, xn,可以给每一个整数取负数或者取原值,求有多少种取法使得这些整数的和等于期望值 E。请写出程序,并解释解题思路。
输入样例
非负整数序列为 1 1 1 1 1 3 (期望值 E 为 3)
输出样例
5
输出描述:
5 种取法分别为:
-1+1+1+1+1 = 3
1-1+1+1+1 = 3
1+1-1+1+1 = 3
1+1+1-1+1 = 3
1+1+1+1-1 = 3
题解
:本题是动态规划目标和问题,假设加法的总和为x,那么减法对应的总和就是sum - x。所以我们要求的是 x - (sum - x) = target,即x = (target + sum) / 2,此时问题就转化为,用nums装满容量为x的背包,有几种方法。这里的x,就是bagSize,也就是我们后面要求的背包容量。
(1)确定dp数组及其下标含义:dp[j],表示:填满j(包括j)这么大容积的包,有dp[j]种方法;
(2)确定递推公式:dp[j] += dp[j - nums[i]];
(3)确定初始化方式:dp[0] = 1;
(4)遍历顺序:遍历物品放在外循环,遍历背包在内循环,且内循环倒序(为了保证物品只使用一次);
(5)举例推导dp数组
代码(C++)
:
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<int> nums;
int data; // 输入数据
// 输入
while(cin >> data){
nums.push_back(data);
if(getchar() == '\n') break;
}
int target = nums[nums.size() - 1];
nums.pop_back();
int size = nums.size();
int sum = 0;
for(int i = 0;i < size;i++){ // 求总和
sum += nums[i];
}
int bagsize = (sum + target) / 2;
if((sum + target) % 2) return 0; //加法和不为整数,一定没有方案
if(sum < abs(target)) return 0; //运算结果一定不得target
vector<int> dp(bagsize+1,0); //初始化数组,使表达式结果为target的方法数目
dp[0] = 1; //初始化
for(int i = 0;i < size;i++){ //遍历
for(int j = bagsize;j >= nums[i];j--){
dp[j] = dp[j] + dp[j-nums[i]]; //递推公式
}
}
// 输出
printf("%d\n", dp[bagsize]);
return 0;
}
写在后面
这个专栏主要是我在刷题的过程中总结的一些笔记,因为我学的也很一般,如果有错误和不足之处,还望大家在评论区指出。希望能给大家的学习带来一点帮助,共同进步!!!