长度为n的数组,取其中k个,要求和为sum,求有多少种取法

长度为n的数组,取其中k个,要求和为sum,求有多少种取法

public class CountKequalSum {
	public static void main(String[] args) {
		int[] arr = {1,3,2,2};
		int k = 2;
		int sum = 4;
		int r = solution(arr,arr.length-1,k,sum);
		System.out.println(r);
	}
	static int solution(int[] arr,int end,int k, int sum){ //arr数组中,前end个数,取k个,和为sum的组合种类(取法)
		if(end+1 < k||end<0||k<0)
			return 0;
		if(sum == 0 && k==0)
			return 1;
		if(k==0 && sum!=0)
			return 0;
		
		int[][] result = new int[2][end+1];
		result[0][k-1] = 0;
		result[1][k-1] = init(arr,end, k, sum);		
		
		for (int i = k; i <= end; i++) {                         //状态递推公式
			result[0][i] = result[0][i-1]+result[1][i-1];        //result[0][i] 表示第i个数arr[i]不取时,有多少种组合
			result[1][i] = solution(arr, i-1, k-1, sum-arr[i]);    //result[1][i]  表示第i个数取时,有多少中组合
		}		
		return result[0][end]+result[1][end];		              
	}
	static int init(int[] arr,int end, int k, int sum){         //初值初始化计算
		if(sum == 0 && k==0)
			return 1;
		if( end < 0|| k < 1 ||end+1 < k)
			return 0;		
			int count = 0;
			for(int i = 0;i < k; i++){
				count += arr[i];
			}
			if(count == sum)
				return 1;
			return 0;		
	}

}

递归解法,第一选或者不选,两种情况的和为答案

 

package 算法;

public class CountKequalSumRecursion {
	public static void main(String[] args) {
		int[] arr = {1,3,2,2};
		int k = 2;
		int sum = 4;
		System.out.println(solution(arr, 0, k, sum));
		
	}
	static int solution(int[] arr,int begin, int k , int sum){
		if(begin > arr.length)
			return 0;		
		if(k==0 && sum != 0)
			return 0;		
		if(k == 0 && sum == 0)
			return 1;		
		if(begin >= arr.length)
			return 0;	
		return solution(arr, begin+1, k-1,sum-arr[begin])+solution(arr, begin+1, k, sum);//第i个选,或者第i个不选,两者相加		
	}

}

 

 

 

 

 

 

 

 

 

<think>我们正在讨论的是“后缀数组”(Suffix Sum Array),注意与“后缀数组”(Suffix Array)不同。后缀数组是用于快速计算从某个位置到数组末尾的所有元素之的预处理数组。而后缀数组是字符串处理中的一种数据结构,用于存储字符串的所有后缀的排序信息。 根据用户的问题,他想要了解如何计算一个长度为n的数组的后缀数组。我们之前已经给出了C++的实现,现在需要更详细地解释计算步骤。 计算步骤: 1. 创建一个长度为n+1的数组(通常命名为suffixSum),用于存储后缀。其中,suffixSum[i]表示从索引i开始到数组末尾(即索引n-1)的所有元素之。我们设置suffixSum[n]=0作为边界条件(即从n开始没有元素,为0)。 2. 从后向前遍历原数组(即从索引n-1到0): - 初始化suffixSum[n]=0 - 对于每个索引i(从n-1递减到0),计算: suffixSum[i] = arr[i] + suffixSum[i+1] 这样,我们只需要O(n)的时间复杂度就可以计算出整个后缀数组。 下面是一个具体的例子: 假设原数组arr = [1, 2, 3, 4, 5],则后缀数组计算过程如下: - 初始化:suffixSum[5]=0 - i=4: suffixSum[4] = arr[4] + suffixSum[5] = 5 + 0 = 5 - i=3: suffixSum[3] = arr[3] + suffixSum[4] = 4 + 5 = 9 - i=2: suffixSum[2] = arr[2] + suffixSum[3] = 3 + 9 = 12 - i=1: suffixSum[1] = arr[1] + suffixSum[2] = 2 + 12 = 14 - i=0: suffixSum[0] = arr[0] + suffixSum[1] = 1 + 14 = 15 因此,后缀数组为:[15, 14, 12, 9, 5] 注意:我们通常只需要0到n-1的位置,所以suffixSum数组的前n个元素就是我们需要的。但为了方便计算,我们设置了第n个元素(索引n)为0。 在C++中,我们可以这样实现: ```cpp vector<long long> buildSuffixSum(const vector<int>& arr) { int n = arr.size(); vector<long long> suffixSum(n+1, 0); // 大小为n+1,初始化所有元素为0 // 从后向前遍历 for (int i = n-1; i >= 0; i--) { suffixSum[i] = arr[i] + suffixSum[i+1]; } return suffixSum; } ``` 注意:使用long long类型可以避免大数溢出。 应用:当我们想要查询从索引i到数组末尾的时,只需要返回suffixSum[i]即可。 例如,查询从索引2到末尾的:suffixSum[2] = 12,即3+4+5=12。 相关问题: 1. 后缀数组与前缀数组有什么相似之处不同之处? 2. 后缀数组在哪些算法问题中可以应用? 3. 如何利用后缀数组优化动态规划问题? 4. 后缀数组的空间复杂度是多少?有没有办法优化空间?</think>### 计算数组的后缀数组 后缀数组(Suffix Sum Array)是一种预处理技术,用于快速计算从任意位置到数组末尾的子数组。计算步骤如下: #### **算法步骤** 1. **初始化**:创建长度 $n+1$ 的数组 `suffixSum`,其中 `suffixSum[n] = 0`(边界条件) 2. **反向遍历**:从索引 $n-1$ 开始向前遍历到 $0$: $$ \text{suffixSum}[i] = \text{arr}[i] + \text{suffixSum}[i+1] $$ 3. **结果**:`suffixSum[0..n-1]` 即为所,其中 $\text{suffixSum}[i]$ 表示子数组 $[i, n-1]$ 的 #### **C++实现** ```cpp #include <iostream> #include <vector> using namespace std; vector<long long> buildSuffixSum(const vector<int>& arr) { int n = arr.size(); vector<long long> suffixSum(n + 1, 0); // 包含边界位置 // 反向遍历计算(从n-1到0) for (int i = n - 1; i >= 0; i--) { suffixSum[i] = arr[i] + suffixSum[i + 1]; } return suffixSum; } int main() { vector<int> arr = {3, 1, 4, 2, 5}; auto suffixSum = buildSuffixSum(arr); // 输出后缀数组 for (int i = 0; i < arr.size(); i++) { cout << suffixSum[i] << " "; // 输出:15 12 11 7 5 } } ``` #### **示例解析** - 输入数组:`[3, 1, 4, 2, 5]` - 计算过程: - `suffixSum[4] = 5 + 0 = 5` - `suffixSum[3] = 2 + 5 = 7` - `suffixSum[2] = 4 + 7 = 11` - `suffixSum[1] = 1 + 11 = 12` - `suffixSum[0] = 3 + 12 = 15` - 结果:`[15, 12, 11, 7, 5]` #### **关键特性** 1. **时间复杂度**:$O(n)$ 预处理,$O(1)$ 单次查询 2. **空间复杂度**:$O(n)$ 3. **查询方式**:获数组 $[i, j]$ 的需结合前缀: $$ \text{sum}(i, j) = \text{prefixSum}[j] - \text{prefixSum}[i-1] $$ 或直接使用后缀计算: $$ \text{sum}(i, j) = \text{suffixSum}[i] - \text{suffixSum}[j+1] $$ #### **应用场景** - 快速计算数组末尾子段(如统计后 $k$ 个元素平均值) - 动态规划优化(如最大子数组问题) - 滑动窗口问题中窗口从尾部开始移动时[^2] --- ### 相关问题 1. 后缀数组与前缀数组在性能上有何差异?如何根据问题特性选择? 2. 如何用后缀优化"最大子数组"问题的动态规划解法?请提供代码示例 3. 后缀数组在滑动窗口问题中的具体应用场景有哪些? 4. 后缀数组(Suffix Array)与后缀数组有何本质区别?[^1][^3]
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值