java
几个简单的问题,运用深搜(递归)解决
1. N个整数划分为K个子集
2. 在N个整数中选取M个数,使其和等指定值
3. 在N个整数中选取任意个数,使其和等于指定值
4. 在N个整数集合中,将集合划分为两部分,且子集交集为空,使其子集的和尽量相等
import java.util.Arrays;
import com.util.Array;
import com.util.Print;
/**
*2017年12月20日 下午12:44:22<br>
*TODO:<pre><br>
* </pre>
Question:<pre><br> </pre>
Analysis:<pre><br> </pre>
*/
public class 整数划分
{
public static void main(String[] args)
{
// s1_test();
// s2_test();
// s3_test();
s4_test();
}
public static void s1_test()
{
int i = -1;
int n = 10;
while(++i<=n+1)
{
System.out.println(n+" "+i+" "+number_split1(n,i));
}
}
/**
* TODO:<pre>
* N个整数划分为K个子集,每个子集不为空且两两交集为空
*求,有多少种划分方法
*考虑一个子集i,如果i单独构成一个子集,那么分法 c(n-1,k-1)
*如果i不单独构成一个子集,那么k*c(n-1,k)
*边界条件:
*( n==k||(n>0&&k==1) ) return 1;
*( n<k||(n>0&&k==0) ) return 0;
* @param n
* @param k
*/
public static int number_split1(int n,int k)
{
if( n==k||(n>0&&k==1) ) return 1;
if( n<k||(n>0&&k==0) ) return 0;
int sum = 0;
sum = number_split1(n-1,k-1)+k*number_split1(n-1,k);
return sum;
}
static int arr[];
static int ans[];
static int tot;
public static void s2_test()
{
int len = 20,sum = 30,k = 5;
arr = Array.getRandomIntArrayNoRepeat(1,22,len);
ans = new int[k+1];
Arrays.sort(arr);
tot = 0;
Print.printArray(arr);
number_split2(sum,k,len-1);
System.out.println(tot);
}
/**
* TODO:<pre>整数划分2
* 在N个整数中选取M个数,使其和等指定值
* 分析:
* 1.递归
* 先排序,然后对于元素i,我们考虑选取它
* 然后方程变为d(sum-i,m-1)
* 不选取:d(sum,m-1)
* 边界:
* sum==0&&m==0 return true
* sum<0||m<0,index<0 ||arr[index]>sum return false
* @param sum 当前的数字和
* @param k 选取K个整数
* @param index 数组下标
*/
public static void number_split2(int sum,int m,int index)
{
if( sum==0&&m==0 )
{
tot++;
Print.printArray(ans,1,ans.length-1);
return;
}
if( sum<0||m<0||index<0||arr[index]>sum ) return;
ans[m] = arr[index];
number_split2(sum-arr[index],m-1,index-1);
ans[m] = 0;
number_split2(sum,m,index-1);
}
public static void s3_test()
{
int len = 20,sum = 30;
arr = Array.getRandomIntArrayNoRepeat(1,22,len);
ans = new int[len];
Arrays.sort(arr);
tot = 0;
Print.printArray(arr);
number_split3(sum,0,len-1);
System.out.println(tot);
}
/**
* TODO:<pre>
* 整数划分3
* 在N个整数中选取任意个数,使其和等于指定值
* @param sum 和
* @param counter 选中的数字个数
* @param index 数组当前下标
*/
public static void number_split3(int sum,int counter,int index)
{
if( sum==0 )
{
tot++;
Print.printArray(ans,0,counter-1);
return;
}
if( sum<0||index<0||arr[index]>sum ) return;
ans[counter] = arr[index];
number_split3(sum-arr[index],counter+1,index-1);
ans[counter] = 0;
number_split3(sum,counter,index-1);
}
static int s4M = 2;// s4 阈值
public static void s4_test()
{
int len = 10,sum = 0;
arr = Array.getRandomIntArrayNoRepeat(1,10,len);
ans = new int[len];
Arrays.sort(arr);
tot = 0;
Print.printArray(arr);
for(int i = 0;i<len;i++)
{
sum += arr[i];
}
number_split4(sum/2,0,len-1);
System.out.println(tot);
}
/**
* TODO:<pre>
* 整数划分4
* 在N个整数集合中,将集合划分为两部分,且子集交集为空,使其子集的和尽量相等
* 如果和的差超过某个阈值,则输出不能完成信息
* 分析:
* 与上面的整数划分类似,将数组求和sum;然后number_split3(sum/2,0,len-1),
*/
public static void number_split4(int sum,int counter,int index)
{
if( sum==0||Math.abs(sum)<=s4M )
{
tot++;
ans[counter] = sum;// 记录 sum相差值,数组最后一位就是差值了
Print.printArray(ans,0,counter);
}
if( index<0 ) return;
ans[counter] = arr[index];
number_split4(sum-arr[index],counter+1,index-1);
ans[counter] = 0;
number_split4(sum,counter,index-1);
}
}