问题描述:
给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
输入描述:
输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数A[i](32位整数),以空格隔开。
输出描述:
输出所求的方案数
输入例子:
5 15
5 5 10 2 3
输出例子:
4
该题目似乎跟数组的所有组合数组没关系,但是可以按照方法一的思路,先求出数组的所有元素组合成的所有子数组,返回所有子数组的集合,然后遍历这个子数组找到和为目标值的子数组
解法一代码如下:
import java.util.*;
public class Test {
public static void main(String[] args) {
System.out.println("输入数组的元素个数和所求子序列的和:");
Scanner scanner =new Scanner(System.in);
//输入数组长度
int length =scanner.nextInt();
//输入所求的子序列的和
int sum =scanner.nextInt();
//将输入的整数数组转换成List<String> 集合
List<String> alists =new ArrayList<String>();
//输入几就循环几次赋值最后如果次数达不到将会导致线程阻塞 不执行下面的代码
for (int i = 0; i <length; i++) {
int temp =scanner.nextInt();
String tempString =temp+"";
alists.add(tempString);
}
//调用数组求组合子序列的方法
List<List<Object>> listR = str(alists);
//遍历这个方法返回值
System.out.println("返回值长度:"+listR.size()+"===》肯定是2的数组长度次幂");
//记录多少个符合要求;即和为目标值的子序列
int count =0;//计数器
//遍历子序列集合中的每个子序列
System.out.println("输出每个子序列:");
for(List<Object> ss:listR){
int eveSum=0; //每个 子序列中所有值的和
//遍历子序列中的每个值并且转换成int类型
for(Object s:ss){
//原来类型为String
String str =(String) s;
//转换成int类型
int evey =Integer.parseInt(str);
//System.out.print(evey+" ");
//将每个元素加起来
eveSum +=evey;
}
// System.out.println("");
//子序列和为目标值的时候count+1
if(eveSum ==sum)
{
System.out.println("目标序列:"+ss.toString());
count ++;
}
}
System.out.println("目标子数组个数:"+count);
}
/*
* 求数组中所有的组合数组
*/
public static List<List<Object>> str(List<String> list) {
//List结合中装的是每个小的组合list
List<List<Object>> result = new ArrayList<List<Object>>();
//总共的组合个数
long n = (long)Math.pow(2,list.size());
//每个组合的list
List<Object> combine;
//循环0-组合次数
for (long l=0L; l<n; l++) {
//创建每个装list的组合
combine = new ArrayList<Object>();
//遍历数组
for (int i=0; i<list.size(); i++) {
//>>>指的是无符号右移
if ((l>>>i&1) == 1)
combine.add(list.get(i));
}
result.add(combine);
}
return result;
}
}
第一种方法中的>>>无符号移位那句话很难理解因此我在第一种方法的基础上衍生出来了第二种方法 。
第二种 方法的本质就是通过分析我们知道一个长度为N的数组的所有子序列个数是2的N次幂(假设这个值为number),可以通过下面这个表格找到数组元素组合的子数组与number之间的关系
倘若这个数组长度N=4 那么number=16 数组的初始化为char ch[] =new char[]{'a','b','c','d'};
number-1 | number-1的二进制表示 | ch[]数组的可能子序列 |
0 | 0000 | [ ] |
1 | 0001 | [d] |
2 | 0010 | [c] |
3 | 0011 | [c,d] |
4 | 0100 | [b] |
5 | 0101 | [b,d] |
6 | 0110 | [b,c] |
7 | 0111 | [b,c,d] |
8 | 1000 | [d] |
9 | 1001 | [a,d] |
10 | 1010 | [a,c] |
11 | 1011 | [a,c,d] |
12 | 1100 | [a,b] |
13 | 1101 | [a,b,d] |
14 | 1110 | [a,b,c] |
15 | 1111 | [a,b,c,d] |
凡是有1在的小标处都显示处理原数组中的数据
下面是第二种方法的代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/*
* 给定一个有n个正整数的数组A和一个整数sum,求选择数组A中部分数字和为sum的方案数。
当两种选取方案有一个数字的下标不一样,我们就认为是不同的组成方案。
输入描述:
输入为两行:
第一行为两个正整数n(1 ≤ n ≤ 1000),sum(1 ≤ sum ≤ 1000)
第二行为n个正整数A[i](32位整数),以空格隔开。
输出描述:
输出所求的方案数
输入例子:
5 15
5 5 10 2 3
输出例子:
4
*/
public class Test01 {
public static void main(String[] args) {
int []ary =new int[]{2,3,5,5,10};
Scanner scanner =new Scanner(System.in);
// System.out.println("请输入数组个数和所求子数组和:");
int number =scanner.nextInt();
int sum =scanner.nextInt();
int []arry =new int[number];
// System.out.println("请初始化数组:");
for (int i = 0; i < arry.length; i++) {
arry[i] =scanner.nextInt();
}
getResult(arry,sum);
}
private static void getResult(int[] ary,int sum) {
//System.out.println(ary.length);
//所有子数组的个数
long size =(long)(Math.pow(2, ary.length));
List<List<Integer > > lists =new ArrayList<>();
for (int i = 0; i <size; i++)
{
//将i转换成二进制 这是每个二进制数对应的集合
List<Integer > resultList = exchange(i, 2);
// System.out.println();
lists.add(resultList);
}
int max=lists.get(0).size();
for (int i = 0; i < lists.size(); i++) {
if(max<lists.get(i).size()){
max=lists.get(i).size();
}
}
//处理数据
for(int i=0;i<lists.size();i++)
{
//i位置的集合
if(lists.get(i).size()!=max)
{
List<Integer> newLists =new ArrayList<Integer>();
for(int j=0;j<max-lists.get(i).size();j++)
{
newLists.add(0);
}
newLists.addAll(lists.get(i));
lists.set(i, newLists);
}
}
// System.out.println("==========处理之后数据===================");
//测试处理之后的数据
for (int i = 0; i < lists.size(); i++) {
for (int j = 0; j < lists.get(i).size(); j++) {
// System.out.print(lists.get(i).get(j));
}
// System.out.println("");
}
// System.out.println("数组的所有组合的子数组:");
//由二进制数的位数1显示的位置显示出来组合出的所有数组
List<List<Integer>> arrysList =new ArrayList<List<Integer>>();
for (int i = 0; i < size; i++) {
List<Integer> alist =new ArrayList<Integer>();
for (int j = 0; j < lists.get(i).size(); j++) {
if(lists.get(i).get(j)==1)
{
alist.add(ary[j]);
// System.out.print(ary[j]);
}
}
arrysList.add(alist);
// System.out.println();
}
//计算所有子数组中每个的和
// System.out.println("=====================");
List<Integer> sums =new ArrayList<Integer>();
int count =0;//计算器
for(int i=0;i<arrysList.size();i++)
{
int eveysum=0;
for(int j=0;j<arrysList.get(i).size();j++)
{
eveysum +=arrysList.get(i).get(j);
}
// System.out.println(i+"个数组的和:"+eveysum);
//如果每个子数组的和为15
if(eveysum ==sum)
{
count++;
}
}
System.out.println(count);
}
//讲一个数转换成二进制的数
public static List<Integer> exchange(int number,int m)
{
List<Integer> lists =new ArrayList<>();
List<Integer> _lists=new ArrayList<Integer>();
int s =number/m;
lists.add(number%m);
while(s>0) {
lists.add(s%m);
s =s/m;
}
for(int i=lists.size()-1;i>-1;i--)
{
_lists.add(lists.get(i));
if(lists.get(i)>9)
{
int cha=lists.get(i)-9;
int temp=cha+65;
char ch =(char) temp;
System.out.print(ch);
}
}
for (int i = 0; i < _lists.size(); i++) {
// System.out.print (_lists.get(i));
}
return _lists;
}
}