整数的任意拆分问题(允许重复)
问题:输入两个整数n 和m,从数列1,2,3.......n 中随意取几个数(允许重复取同一个数), 使其和等于m ,要求将其中所有的可能组合列出来.
分析:记整数k的所有组合为C(k),则K可以表示为以下的组合:
{K} U C(0)
{K-1} U C(1)
{K-2} U C(2)
....
{1} U C(k-1)
以上是K<=n的情况, 如果k>n,则K可以表示为以下的组合:
{n} U C(K-n)
{n-1} U C(K-n+1)
{n-2} U C(K-n+2)
....
{1} U C(k-1)
因此可以使用动态规划的方法解决。
public void sumPartitions(int m,int n){
this.m = m;
this.n = n;
C = new Object[m+1];
C[0]=new int[][]{{}};
C[1]=new int[][]{{1}};//C[2]=new int[][]{{1,1},{2}};
for(int i=2;i<=m;i++){
List<int[]> combines = new ArrayList<int[]>();
int jStart = (i<=n)?i:n;
for(int j=jStart;j>=1;j--){
Object[] cArray = (Object[])C[i-j];
for(int k=0;k<cArray.length;k++){
int[] cElement = (int[])cArray[k];
int[] cElementNew = new int[cElement.length+1];
System.arraycopy(cElement, 0, cElementNew, 0, cElement.length);
cElementNew[cElement.length] = j;
combines.add(cElementNew);
}
}
C[i] = combines.toArray();
}
}
public void printSize(){
Object[] ci = (Object[])C[m];
System.out.println("m:"+ m + ",n=" + n + " size:" + ci.length);
}
public void prettyPrint(){
//for(int i=0;i<=m;i++){
Object[] ci = (Object[])C[m];
System.out.print("{");
for(int j=0;j<ci.length;j++){
int[] cij = (int[])ci[j];
System.out.print("{");
for(int k=0;k<cij.length-1;k++)
System.out.print(cij[k] + ",");
if(cij.length-1>=0)
System.out.print(cij[cij.length-1]);
System.out.print("}");
}
System.out.print("}");
//System.out.print(" m:"+ m + ",n=" + n + " size:" + ci.length);
System.out.println();
//}
}
public static void main(String[] args) {
SumPartition sp = new SumPartition();
int m=5;
int n=3;
sp.sumPartitions(m,n);
sp.printSize();
sp.prettyPrint();
m = 20;
n=10;
sp.sumPartitions(m,n);
sp.printSize();
}
}
测试输出:
m:5,n=3 size:13
{{2,3}{1,1,3}{3,2}{1,2,2}{2,1,2}{1,1,1,2}{1,3,1}{2,2,1}{1,1,2,1}{3,1,1}{1,2,1,1}{2,1,1,1}{1,1,1,1,1}}
m:20,n=10 size:521472
注意输出中重复的集合{2,3}和{3,2}, 还有{1,2,2}, {2,1, 2}。可以在最后加上去重复集合的功能过滤掉。