给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数

题目:

给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。

当两种选取方案有一个数字的下标不一样,就认为是不同的组成方案。

分析:

首先确定选择的个数,有n种这样的选择,分别是选择1个数,2个数…n个数。分别对应m1个选择方案,m2个选择方案…mn个选择方案。

所以,我们先写这样一个方法,计算m1+m2+…+mn,和就是我们求的总的方案数。

该方法需要两个参数,也就是两个初始条件,一个是数组A,一个是和sum.

	public static int getAllSchemeNum(int[] arr,int sum)
	{
		int count=0;
		//将选择一个数,两个数...n个数时的方案数相加
		for(int num=1;num<=arr.length;num++)
		{
			//getNumIfChosseM是选择m个数时得到的方案数
			count+=getSchemeNumIfChooseM(arr,num,0,sum);
		}
		return count;
	}

现在,重点是如何编写getSchemeNumIfChooseM方法。

如果我们选择k个数,并规定这些数的顺序与数组中的顺序相同,那么只要确定每个数的位置就ok了,我们先确定第一个数的下标,范围是[0,n-k],为什么可选下标的右边界是n-k,因为如果第一个数的下标是n-k+1,那么后面只有k-2个位置,放不下剩余的k-1个值。

假定第一个数选择的下标为b1(b1<=n-k),再选第2个数的位置,第二个数只能在第一个数之后,因此它可选的下标范围[b1+1,n-k+1];

依次类推,直到第k个数被选定。

用递归是较好的办法。

每次递归,必须给出,数组A,还要选定的数的个数,可选下标的左边界,以及还要选定的数之和。

	/**
	 * 
	 * @param arr		数组A
	 * @param num		还需要选择的数的个数
	 * @param index		可选的范围的左边界
	 * @param sum		还需要选择数之和
	 * @return
	 */
	public static int getSchemeNumIfChooseM(int[] arr, int num,int index,int sum)
	{
		int count=0;
		
		//如果全部选择完成,则只需判定sum是否为零,如果为零,符合条件,返回1,否则返回0
		if(num==0)
		{
			return sum==0?1:0;
		}
		//剩余要选的数里,第一个数可选的范围为[index,arr.length-num]
		for(int i=index;i<=arr.length-num;i++)
		{
			if(arr[i]<=sum)
				//可选的个数减一,可选的左边界等于当前确定数的小标加1,
				count+=getSchemeNumIfChooseM(arr,num-1,i+1,sum-arr[i]);
		}
		
		return count;
	}

自此,所有代码以及完成。完整及测试代码如下:

public class Test {

	public static void main(String[] args) throws Exception 
	{
		int[] arr ={5,5,5,2,3};
		System.out.println("方案数:"+getAllSchemeNum(arr,15));
	}
	
	public static int getAllSchemeNum(int[] arr,int sum)
	{
		int count=0;
		//将选择一个数,两个数...n个数时的方案数相加
		for(int num=1;num<=arr.length;num++)
		{
			//getNumIfChosseM是选择m个数时得到的方案数
			count+=getSchemeNumIfChooseM(arr,num,0,sum);
		}
		return count;
	}
	
	/**
	 * 
	 * @param arr		数组A
	 * @param num		还需要选择的数的个数
	 * @param index		可选的范围的左边界
	 * @param sum		还需要选择数之和
	 * @return
	 */
	public static int getSchemeNumIfChooseM(int[] arr, int num,int index,int sum)
	{
		int count=0;
		
		//如果全部选择完成,则只需判定sum是否为零,如果为零,符合条件,返回1,否则返回0
		if(num==0)
		{
			return sum==0?1:0;
		}
		//剩余要选的数里,第一个数可选的范围为[index,arr.length-num]
		for(int i=index;i<=arr.length-num;i++)
		{
			if(arr[i]<=sum)
				//可选的个数减一,可选的左边界等于当前确定数的小标加1,
				count+=getSchemeNumIfChooseM(arr,num-1,i+1,sum-arr[i]);
		}
		
		return count;
	}
}

输出:

方案数:4

如果有更好的解决方案,请留言,一起讨论,一起进步!

点赞