假设有一个随机数列{10,20,5,......}
求其中相加为100的所数有组合,比方{{20,20,20,20,20},{20,20,20,10,5,5,20},......}
求其中相加为100的所数有组合,比方{{20,20,20,20,20},{20,20,20,10,5,5,20},......}
解决方案 »
- 迷惑的Java多线程问题
- !!!!eclipse和数据库的一个乱码问题 高手指教下!!!!!!!!!!
- 求救:日期比较问题
- 如何在mysql储存二进制图片(java,jsp)
- 在Panel上点击鼠标,如何捕捉鼠标点击点的坐标?急急急!
- 如何做WINDOWS中的菜单\工具栏?
- 熟悉C和C++,想要学习JAVA,该从什么地方入手(从哪些书籍入手比较好)?
- java and JDBC!
- 谁能告诉sql server 驱动程序下载地和驱动drivername是什么
- 菜鸟送分来了.......
- 我注册的按钮事件为何没有反应啊,?请求高手指点啊
- 写一个接口,给人家用httpclient请求,然后以xml形式返回给给人家
package com.haojia.algorithm;import java.util.Random;/**
* 子集求和 非递归回溯 效率高
*
* @author July
*
*/
public class SubSum { // 计算
public static void compute(int[] data, int sum) {
boolean[] state = new boolean[data.length];
int p = 0;
int temp = 0;
int count = 0; while (true) {
// 累加开始
while (p != data.length) {
if (!state[p]) {
state[p] = true;
temp += data[p];
if (temp == sum) {
count++;
print(data, sum, state);
}
if (temp > sum) {
state[p] = false;
temp -= data[p];
}
}
p++;
}
// 累加结束 // 回溯开始
while (state[p - 1]) {
state[p - 1] = false;
temp -= data[p - 1];
p--;
if (p == 0) {
System.out.println(count + "种方法");
return;
}
} while (!state[p - 1]) {
p--;
if (p == 0) {
System.out.println(count + "种方法");
return;
}
} state[p - 1] = false;
temp -= data[p - 1];
// 回溯结束
}
} // 格式化打印
public static void print(int[] data, int sum, boolean[] state) {
StringBuffer s = new StringBuffer();
for (int i = 0; i < data.length; i++) {
if (state[i]) {
s.append(data[i]);
s.append("+");
}
}
System.out.println(s.substring(0, s.length() - 1) + "=" + sum);
} public static void main(String[] args) {
Random r = new Random();
int[] data = new int[20];
for (int i = 0; i < data.length; i++) {
data[i] = r.nextInt(100);
}
compute(data, 100);
}
}
其中最关键的思路是用一个boolean数组来标记随机数组里的数是否被加过
之后便可以通过考察boolean数组的状态进行回溯
如果数组int[] data = new int[]{5,5,5,5,20,20,20,20,10,10,10,5,5,10,10,20,20,10,10,5};
计算结果是65386种,显然不是我们所要求的组合。:另LS的同志些都不运行下结果瞎起哄。回帖不是打个“顶”就OK了,还不如不看呢。
1.给的随机数不能重复的在一种情况下出现(#14符合你的要求,不过代码有误需要修改,我建议你们不要只是把算法发上来,真正的测试运行下)
2.可以重复的出现,比如100%n==0,那么可以是一些n在组成一种组合,如{20,20,20,20,20}(#14代码和算法就不对了)
3.在2中还可以是x个n和y个m组成一个组合,如{20,20,20,20,5,5,5,5}它们相加也是100.请lz说明是以上哪种情况,然后我可以用比较复杂的算法解决(时间复杂度比较高)
3.在2中还可以是x个n和y个m组成一个组合,如{20,20,20,20,5,5,5,5}它们相加也是100. 烦请给出代码,谢谢大虾了,在此也谢谢各楼的关注,希望继续帮忙啊
1.求出所有组合可能。这不难,我已有代码。(当然如果该组合中的数直接相加已经大于100先行舍去。)
2.求出该组合中是否可以相加得到100。这就是个背包问题,只是限制背包不能剩余而已。只要从最小的开始试就行了。这步用一下递归。很抱歉的是,我是从C++区转过来的,Java是初学者,没有能力给你写出代码。如果LZ需要C++的代码请给我留言。
package cxz.math;import java.util.Calendar;public class Cal {
int[] data = { 5, 5, 5, 5, 20, 20, 20, 20, 10, 10, 10, 5, 5, 10, 10, 20, 20, 10, 10, 5 };
int total = 100;
/////////////////
int nt = data.length;
int[] s = new int[nt];
int m[] = { 1, 0 };
int fm = 0;
int no = 0; public static void main(String[] args) {
Calendar cstart = Calendar.getInstance();
Cal g = new Cal();
g.cal();
Calendar cend = Calendar.getInstance();
System.out.println("花费时间" + (cend.getTimeInMillis() - cstart.getTimeInMillis()) / 1000.0 + "秒");
} public void cal() {
fm = 0;
f(0);
} private void f(int n) {
if (n == nt) {
return;
}
for (int i = 0; i < m.length; i++) {
s[n] = m[i];
if (m[i] == 1) {
fm += data[n];
if (fm == total) {
no++;
show();
}
}
if (fm < total)
f(n + 1);
if (m[i] == 1) {
fm -= data[n];
}
}
} void show() {
StringBuffer buf = new StringBuffer();
for (int L = 0; L < data.length; L++)
buf.append((s[L] == 1 ? (data[L] + " ") : ""));
System.out.println(no + ":" + buf.toString());
}
}
int[] data = { 5, 5, 5, 5, 20, 20, 20, 20, 10, 10, 10, 5, 5, 10, 10, 20, 20, 10, 10, 5 };
int total = 100;
/////////////////
int nt = data.length;
int[] s = new int[nt];
int m[] = { 1, 0 };
int fm = 0;
int no = 0; public static void main(String[] args) {
Calendar cstart = Calendar.getInstance();
Cal g = new Cal();
g.cal();
Calendar cend = Calendar.getInstance();
System.out.println("花费时间" + (cend.getTimeInMillis() - cstart.getTimeInMillis()) / 1000.0 + "秒");
} public void cal() {
fm = 0;
f(0);
} private void f(int n) {
if (n == nt) {
return;
}
for (int i = 0; i < m.length; i++) {
s[n] = m[i];
if (m[i] == 1) {
fm += data[n];
if (fm == total) {
no++;
show();
}
}
if (fm < total)
f(n + 1);
if (m[i] == 1) {
fm -= data[n];
}
}
} void show() {
StringBuffer buf = new StringBuffer();
for (int L = 0; L < data.length; L++)
buf.append((s[L] == 1 ? (data[L] + " ") : ""));
System.out.println(no + ":" + buf.toString());
}
}
int num[]={随机数};void func(int i)
{
if(i>=数组大小)
{
//检查标志位为1的num相加是否满足要求。
return;
}flag[i]=0;
func(i+1);
flag[i]=1;
func(i+1);
}结帖率那么低,真不想回答你的问题。
1.首先去掉 大于100 的元素。得到一个新数组;
2.剔除数组中重复的数据,因为本身可以重复取,所以去掉无所谓。
3.然后排序,则数据中的元素个数<=100个,这样数组的长度就比较小的。解决起来应该就方便点至于问题的本质,我正在考虑中。14L的代码没有注释和说明,有点难懂啊。。很有意思,再好好想想看。
1.去掉 >100 的元素。得到一个新组,并除去数组中重复的数据,因为本身可以重复取,所以去掉无所谓。然后排序,则数据中的元素个数<=100;
原始数组为 OrigData,N为所求的和值
设 元素为1-->N;HashTable ht ;存储找到的序列组int[] Prepare(int[] OrigData,int N) //预处理方法,得到处理后的数组,就是步骤1中的,比较简单HashTable GetAllCombination(int[] OrigData,int N)
{
HashTable ht = new HashTable() ;
if(N<=0)
return null ;//返回为空,说明需要找的为0,也就是已经满足条件
int[] Randoms = PrePare(OrigData,N) ;//预处理原始数据
for(int i=0;i<Randoms.Length;i--)
{
int cutValue = Randoms[i] ;//当前数组的值
//获取其除数,因为需要重复,而最大的重复量就是其倍数,可以对除数依次递减,以此来获得重复的数
int n1=N/cutValue ;
int temp = n1*cutValue ;
// if(temp==N) //说明能够整除
//将当前值重复n1次添加到HashTable,作为一条符合条件的记录
for(int j=n1 ; j>0;j--)
{
string cutCombination= GetNcombination(cutValue,j);
HashTable tempHt=GetAllCombination(Randoms,N-j*cutValue) ;
if(tempHt==null)
// 将当前值cutValue 重复n1次添加到HashTable,作为一条符合条件的记录
ht.Add(cutCombination);
else //遍历hash表,将其添加到这之后并作为一条记录
//合并并添加记录
foreach(var a in tempHt)
ht.Add(cutCombination+var) ;
}
}
}//将值cutValue去times次组合的结果
string GetNcombination(int cutValue ,int times)
{
string str="";
for(int i=0;i<times;i++)
{str+=(cutValue.ToString()+",") ;}
return str ;
}
我的思路是采用迭代计算。因为每一个数都可以看成是2个数的和,如果一个数能分解,则把可分解的数代入,得到一个新的组合。从1开始运行,每一步都要搜索所有的组合。
首先的第一步还是进行下列操作:
1.去掉 >N 的元素。得到一个新组,并除去数组中重复的数据,因为本身可以重复取,所以去掉无所谓。然后排序,则数据中的元素个数 <=N ;
根据上面兄弟的测试结果,采用这组数据:
int[] data = new int[]{5,5,5,5,20,20,20,20,10,10,10,5,5,10,10,20,20,10,10,5};
我的程序运行的结果只有36种,而14楼的方法计算结果是65386种。差别这么大,大家手动计算一下就可以了,因为只包括5,10,20这3个元素,可以重复取,这个问题就类似于100元钱,用现有的货币组合,共有多少种。如果只有5,10,20这3种货币,怎么可能组合成那么多?我把计算出的36种结果列出来,大家看看:5,5,5,5,5,5,10,20,20,20
5,5,5,5,5,5,5,5,5,5,10,20,20
5,5,5,5,20,20,20,20
10,10,10,10,10,10,20,20
5,5,5,5,5,5,5,5,5,5,10,10,10,10,10
5,5,5,5,5,5,5,5,10,10,10,10,10,10
5,5,5,5,5,5,5,5,10,10,20,20
20,20,20,20,20
10,10,10,10,20,20,20
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,10
5,5,5,5,5,5,5,5,20,20,20
5,5,5,5,5,5,10,10,10,20,20
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,20
5,5,5,5,10,10,20,20,20
10,10,20,20,20,20
5,5,5,5,10,10,10,10,20,20
5,5,5,5,5,5,5,5,10,10,10,10,20
5,5,10,20,20,20,20
5,5,10,10,10,10,10,10,10,20
5,5,5,5,5,5,10,10,10,10,10,20
5,5,5,5,5,5,10,10,10,10,10,10,10
5,5,5,5,5,5,5,5,5,5,5,5,5,5,10,20
5,5,10,10,10,20,20,20
5,5,5,5,5,5,5,5,5,5,10,10,10,20
10,10,10,10,10,10,10,10,20
5,5,5,5,10,10,10,10,10,10,20
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5
5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,10,10
5,5,5,5,5,5,5,5,5,5,5,5,10,10,10,10
5,5,5,5,5,5,5,5,5,5,5,5,20,20
5,5,5,5,5,5,5,5,5,5,5,5,10,10,20
5,5,10,10,10,10,10,10,10,10,10
5,5,5,5,5,5,5,5,5,5,5,5,5,5,10,10,10
5,5,5,5,10,10,10,10,10,10,10,10
5,5,10,10,10,10,10,20,20
10,10,10,10,10,10,10,10,10,10下面是我的程序计算下列数据的结果:因为100太大了,计算比较慢,把N搞小点,大家也可以手动的分析一下结果的正确性。
int[] arr = new int[] {2,3,4,7,8}; N=10 ;
生成的结果如下:共7组
2,4,4,
2,2,2,4,
3,3,4,
2,2,3,3,
3,7,
2,2,2,2,2,
2,8,程序正在整理中,因为N太大时,速度比较慢,过2天再贴出来。
static int[][] arr;
static int[] arr0;
public static void main(String[] args){
arr = new int[][]{{},{},{},{}};
for(int i = 0 ;i<arr.length;i++)
{
for(int j = 0 ;j<arr[i].length;j++){
arr0[i]=a[i][j];
}
}
for(int k = 0 ;j<arr0.length;k++){if(arr0[k]==100){ System.out.println(arr0[k]);
} }
}}
自己没有测试
不知道对不对~~
可能要定义一个集合重新存储以下