给定33个数 从33个中选6个(可以重复) 加和等于150

cs41yin 2009-11-16 08:47:26
加精
给定33个数 从33个中选6个(可以重复) 加和等于150 统计有多少种排列方法 JAVA怎么编写啊?
谁能解答一下 谢谢
...全文
4102 134 打赏 收藏 转发到动态 举报
写回复
用AI写文章
134 条回复
切换为时间正序
请发表友善的回复…
发表回复
yangit11 2009-12-04
  • 打赏
  • 举报
回复
这33个数本身是有重复还是无重复的?
yangit11 2009-12-04
  • 打赏
  • 举报
回复
动态规划,稍微注意下可重复
jym2002 2009-12-04
  • 打赏
  • 举报
回复
學習了,開了一點窍
CrySleeper 2009-12-03
  • 打赏
  • 举报
回复
应该是P问题,dynamic programming.
相当于在33*6个数里(每个数重复6次)选择6个数的和150。

从第一个数开始, 如果这个数能被选中,那么在剩下的数中选择5个数和为150-a1;如果这个数不能被选中,那么从剩下数中选择6个和为150。

这样一直递归下去,复杂度应该是多项式的
panhaichun 2009-12-03
  • 打赏
  • 举报
回复
[Quote=引用 128 楼 zy88882007 的回复:]
26楼的算法有问题 举个例子吧
10 20 30 30 30 30 也符合啊
[/Quote]

嗯,是有个错误,发现了,修正了,在我电脑上跑[1-33]要400毫秒左右


public static void main(String[] args) throws Exception {
long start = new Date().getTime();
int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33 };
think(0, 150, a, 0, new int[6]);
System.out.println(new Date().getTime() - start);
}

static void think(int i, int totalNow, int[] src, int size, int[] result) {
if (size == 6) {
if (totalNow == 0) {
for (int k = 0; k < result.length; k++)
System.out.print(result[k] + " ");
System.out.println();
}
return;
}
if (src[i] > totalNow) {
return;
}
int[] r = new int[result.length];
System.arraycopy(result, 0, r, 0, r.length);
for (; i < src.length; i++) {
r[size] = src[i];
think(i, totalNow - src[i], src, size + 1, r);
}

}
michaellufhl 2009-12-03
  • 打赏
  • 举报
回复
static List<Integer> data;
static int NUMBER=150;

static void initAndSortData(){
data=new ArrayList<Integer>();
data.add(10);data.add(20);data.add(30);data.add(40);data.add(50);。。。
Collections.sort(data);//Asc order
System.out.println(data);
}

public static int sum(int ... d ){
int result=0;
for(int i:d){
result+=data.get(i);
}
return result;
}

public static void main(String[] args) {
initAndSortData();
for(int i1=0;i1<data.size();i1++){
if(i1>=NUMBER){
break;
}
for(int i2=i1;i2<data.size();i2++){
if(sum( i1 , i2)>=NUMBER){//Break, if ready greater than 150
break;
}
for(int i3=i2;i3<data.size();i3++){
if(sum( i1 , i2, i3)>=NUMBER){
break;
}
for(int i4=i3;i4<data.size();i4++){
if(sum( i1 , i2, i3, i4)>=NUMBER){
break;
}
for(int i5=i4;i5<data.size();i5++){
if(sum( i1 , i2, i3, i4,i5)>=NUMBER){
break;
}
for(int i6=i5;i6<data.size();i6++){

if(sum(i1,i2,i3,i4,i5,i6)>NUMBER){
break;
} else if(sum(i1,i2,i3,i4,i5,i6)==NUMBER){//Fit!
System.out.println(Arrays.toString(new Integer[]{data.get(i1),data.get(i2),data.get(i3),data.get(i4),data.get(i5),data.get(i6)}));
}
}
}
}
}
}
}
}
zy88882007 2009-12-03
  • 打赏
  • 举报
回复
26楼的算法有问题 举个例子吧
10 20 30 30 30 30 也符合啊
panhaichun 2009-11-25
  • 打赏
  • 举报
回复
124#
不好意思,刚才看走眼了,你的程序没我说的那种问题
panhaichun 2009-11-25
  • 打赏
  • 举报
回复
to 124#

看你的程序貌似没有考虑这种情况:

26 26 26 24 24 24 和 24 24 26 26 24 26 这2组解是重复的,应该只有一组才有效
panhaichun 2009-11-25
  • 打赏
  • 举报
回复
[Quote=引用 124 楼 heguodong 的回复:]
。。。。。。
* 在调试版本下大约需要:296.875毫秒秒时间
* 在Release版本下该精度的时间统计不了,也就是效益0.0001毫秒

[/Quote]

我在26楼的算法,运行[1-33]这33个数的平均时间在200毫秒左右,
heguodong 2009-11-25
  • 打赏
  • 举报
回复
/* 谢谢123楼的证明,我想简单了,下面有新做法,请指正
* 我看有很多人回答了各自的思路,基本上思路都是穷举6个数字,看加起来等于不等于150,但是存在一个严重的问题,就是直接组合的话规模太大,33的6次方个组合=12,914,679,69个组合,
* 每次组合都需要判断 数字1+数字2+数字3+数字4+数字5+数字6是不是等于150
* 类似于下面的方法
for(int i1=1;i1 <33;++i1)
for(int i2=1;i2 <33;++i2)
for(int i3=1;i3 <33;++i3)
for(int i4=1;i4 <33;++i4)
for(int i5=1;i5 <33;++i5)
for (int i6 = 1; i6 < 33; ++i6)
{
int sum =i1+i2+i3+i4+i5+i6;
if(sum!=150)continue ;
}
* 这种方式在本机器经过测试,没有任何输出的情况下大约需要10秒的时间
* 在需要用户交互的程序里面,10秒钟的等待基本上会让人抓狂,痛定思痛,必须找个改进的方法
* 想到了弹簧,6个弹簧串起来,长度固定在150,这6根弹簧有如下特定
* 1,长度最长能拉到33
* 2,最短能压缩到1
* 3,弹簧的压缩系数相同
* 4,弹簧的自身的重量忽略不计
* 在弹簧的两端加上两个重物,那么我考虑这串弹簧的两个状态,水平状态和垂直状态
* 1,在水平状态下,每根弹簧的长度肯定都是25,不考虑摩擦,即[25,25,25,25,25,25]
* 2,在垂直状态下,弹簧受重物拉伸,弹簧长度就会变为[33,33,33,33,17,1],也就是往一边压缩到不能压缩为止
* 受此启发,想到一个不浪费CPU的新算法,该算法只计算所有的从大到小的组合排列,因为前提说明了不关心顺序
* 1,将长度为6的整型数组长度初始化为平均值(150除6=25),如果不能整除,则保证大的数字在前面,比如要求总长度是153的话,那么就是[26,26,26,25,25,25]
* 2,将第一个整数加1,然后对后面的5个数字继续用150减2的值6求平均,再对第二个数字加1,然后对后面的4个数字用150-26-26的值求平均,递归这个过程,一直到前5个数字都不能再加1为止,也就是成了[33,33,33,33,17,1]
* 3,递归的约束条件:a,所有的数字大于0但是小于34. b,对任意大于0小于6的整数n,满足Arr[n]<=Arr[n-1],也就是后一个不能超过前一个
* 3,递归的结束条件是:剩余的可以分配到后续弹簧上的长度小于后面弹簧的个数,或者后面剩下的弹簧个数为1
*
* 此算法的正确性可以数学证明
* 算法性能,当前问题
* 按照当前问题的条件
* int sum = 150;
int count = 6;
int max = 33;
int min = 1;
* 在调试版本下大约需要:296.875毫秒秒时间
* 在Release版本下该精度的时间统计不了,也就是效益0.0001毫秒
* 差别在于Release版本下没有输出
*/

using System;
using System.Collections.Generic;
using System.Text;

namespace FindNum
{
class Program
{
//算法第一步的求平均值得算法1
static int[] MakeMostAvgArray(int sum, int len)
{
int[] ret=new int[len];
MakeMostAvgArray(ret, sum, 0);
return ret;
}
//算法第一步的求平均值得算法2
static void MakeMostAvgArray(int[] data,int sum, int cursor)
{
int len = data.Length - cursor;
int avg = sum / len;
int mod = sum % len;
for (int i = cursor; i < data .Length; ++i)
data[i] = avg;
for (int i = cursor; i < mod+cursor; ++i)
++data[i];
}
/// <summary>
/// 算法第二步:递归
/// </summary>
/// <param name="data">数组</param>
/// <param name="sum">要求的和,这个题目而言要求的是和等于150</param>
/// <param name="cursor">当前递归到第几个数字</param>
/// <param name="max">所有数的最大值,这里为33</param>
/// <param name="min">所有数的最小指,这里为1</param>
static void DoMakeNum(int[] data,int sum,int cursor,int max,int min)
{
System .Diagnostics .Debug .Assert(max >=0);
if (sum / (data.Length - cursor) < min) return;//递归的结束条件1:剩余的可以分配到后续弹簧上的长度小于后面弹簧的个数
if (cursor + 1 == data.Length)//递归的结束条件2:或者后面剩下的弹簧个数为1
#if DEBUG
{
for (int i = 0; i < data.Length; i++)
{
Console.Write("{0} ", data[i]);
}
Console.WriteLine();
return;
}
#endif

for (;TestCursor(data,cursor,max); ++data[cursor])
{
int newSum=sum - data[cursor];
MakeMostAvgArray(data, newSum, cursor + 1);
DoMakeNum(data, newSum, cursor + 1, max,min);
}
}
//递归的约束条件
static bool TestCursor(int[] data, int cursor, int max)
{

if (data[cursor] > max) return false;//a,所有的数字大于0但是小于34
if (cursor == 0) return true;//第一个数字不受第二个约束条件限制
if (data[cursor] > data[cursor - 1]) return false;//b,对任意大于0小于6的整数n,满足Arr[n]<=Arr[n-1],也就是后一个不能超过前一个
return true;
}
private static void MakeNum(int sum, int count, int max, int min)
{
int[] data = MakeMostAvgArray(sum, count);
DoMakeNum(data, sum, 0, max, min);
}
static void Main(string[] args)
{
DateTime start = DateTime.Now;
int sum = 150;
int count = 6;
int max = 33;
int min = 1;
MakeNum(sum, count, max, min);

DateTime end = DateTime.Now;
TimeSpan ts = end - start;
Console.WriteLine(ts.TotalSeconds);
Console.WriteLine(ts.TotalMilliseconds);
Console.Read();
}
}
}



测试结果:有7352个组合,第一个组合为[25 25 25 25 25 25 ],最后一个组合为[33 33 33 33 17 1]

调整以下调教,如果所有的数字都要大于23,则所有组合如下

25 25 25 25 25 25
26 25 25 25 25 24
26 26 25 25 24 24
26 26 26 24 24 24
27 25 25 25 24 24
27 26 25 24 24 24
27 27 24 24 24 24
28 25 25 24 24 24
28 26 24 24 24 24
29 25 24 24 24 24
30 24 24 24 24 24

其他的没有测试

heguodong 2009-11-23
  • 打赏
  • 举报
回复
如果算法有问题,请大家多指正
heguodong 2009-11-23
  • 打赏
  • 举报
回复
using System;
using System.Collections.Generic;
using System.Text;

/* 直接根据排列组合的话量级很大,33的6次方的量级,在这个量级下做一个长时间计算的后台程序还可以,如果需要跟前台界面密切结合,反应就太慢了

* 所以想到了我们经常用的弹簧,用6个弹簧模拟这个问题
* 思路:每根弹簧的最大长度是33,最小长度是1,把6根弹簧串起来,总共长度保持在150,起始状态是每根弹簧长度25
*
* 然后我们可以开始往一边推弹簧,第一个推倒最大长度33后推第二个,一直重复道最后一根不能推动为止
* 记录整个过程中每根弹簧的长度,就是我们要的6个数字
* 考虑到两边是对称的,往两边推出来的长度是重复的,素以只需要往一边推
*/
//模拟单个弹簧的类
class Spring
{
int m_MinLength;
int m_MaxLength;
int m_Length;
public int Length
{
get { return m_Length; }
}
public Spring(int min, int max,int len)
{
System.Diagnostics.Debug.Assert(min > 0);
System.Diagnostics.Debug.Assert(len <= 33);
System.Diagnostics.Debug.Assert(len > 0);
System.Diagnostics.Debug.Assert(max <= 33);
System.Diagnostics.Debug.Assert(max >= min);
m_MinLength = min;
m_MaxLength = max;
m_Length = len;
}
//判断是否可以被拉长
public bool CanLength()
{
if (m_Length >= 33) return false;//超过33就不能继续拉长了
return true;
}
//拉长弹簧
public void Lengthen()
{
++m_Length;
}
//判断弹簧是否可以被压缩
public bool CanShort()
{
if (m_Length <= 1) return false;
return true;
}
//缩短弹簧
public void Shorten()
{
--m_Length;
}
}

//模拟一组弹簧串联
public class SpringConnect
{
readonly int m_FixLength;//串联起来的弹簧的固定不变得长度和
List<Spring> m_Springs;//串起来的弹簧串
/// <summary>
/// 构造函数
/// </summary>
/// <param name="totalLen">固定的总长度</param>
/// <param name="springCount">弹簧的个数</param>
/// <param name="springLen">弹簧的初始长度</param>
public SpringConnect(int totalLen,int springCount,int springLen)
{
System.Diagnostics.Debug.Assert(totalLen > 0);
m_FixLength = totalLen;
m_Springs = new List<Spring>();
for (int i = 0; i < springCount; ++i)
{
Spring sp = new Spring(1, 33, springLen);
m_Springs.Add(sp);
}
}
//拉动弹簧,从一边往另一边顺序拉动弹簧
public void DragSpring()
{
//因为要保证总体长度不变,所以最后一根弹簧不能依靠自身的操作变化长度,只能由前面的弹簧改变长度
Spring curSpring;//当前正在被操作的弹簧
for (int i = 0; i < m_Springs.Count - 1; ++i)
{
curSpring = m_Springs[i];
Spring nxtSpring = m_Springs[i + 1];
while (true)
{
if (!curSpring.CanLength()) break;//如果当前弹簧不能被拉长,则继续下一个弹簧
if (!nxtSpring.CanShort()) break;//如果紧接在后面的弹簧不能被压缩,则继续下一个弹簧
curSpring.Lengthen();//拉长当前弹簧
nxtSpring.Shorten();//缩短后续弹簧
//输出当前的一组数字
for (int j = 0; j < m_Springs.Count; ++j)
{
Console.Write(m_Springs[j].Length);
Console.Write(" ");
}
Console.Write("\r\n");
}
}
}
}
namespace findnum
{
class Program
{
static void Main(string[] args)
{
SpringConnect cn = new SpringConnect(150, 6, 25);
cn.DragSpring();
}
}
}

//在VS2005环境下测试过
最终的结果如下
26 24 25 25 25 25
27 23 25 25 25 25
28 22 25 25 25 25
29 21 25 25 25 25
30 20 25 25 25 25
31 19 25 25 25 25
32 18 25 25 25 25
33 17 25 25 25 25
33 18 24 25 25 25
33 19 23 25 25 25
33 20 22 25 25 25
33 21 21 25 25 25
33 22 20 25 25 25
33 23 19 25 25 25
33 24 18 25 25 25
33 25 17 25 25 25
33 26 16 25 25 25
33 27 15 25 25 25
33 28 14 25 25 25
33 29 13 25 25 25
33 30 12 25 25 25
33 31 11 25 25 25
33 32 10 25 25 25
33 33 9 25 25 25
33 33 10 24 25 25
33 33 11 23 25 25
33 33 12 22 25 25
33 33 13 21 25 25
33 33 14 20 25 25
33 33 15 19 25 25
33 33 16 18 25 25
33 33 17 17 25 25
33 33 18 16 25 25
33 33 19 15 25 25
33 33 20 14 25 25
33 33 21 13 25 25
33 33 22 12 25 25
33 33 23 11 25 25
33 33 24 10 25 25
33 33 25 9 25 25
33 33 26 8 25 25
33 33 27 7 25 25
33 33 28 6 25 25
33 33 29 5 25 25
33 33 30 4 25 25
33 33 31 3 25 25
33 33 32 2 25 25
33 33 33 1 25 25
33 33 33 2 24 25
33 33 33 3 23 25
33 33 33 4 22 25
33 33 33 5 21 25
33 33 33 6 20 25
33 33 33 7 19 25
33 33 33 8 18 25
33 33 33 9 17 25
33 33 33 10 16 25
33 33 33 11 15 25
33 33 33 12 14 25
33 33 33 13 13 25
33 33 33 14 12 25
33 33 33 15 11 25
33 33 33 16 10 25
33 33 33 17 9 25
33 33 33 18 8 25
33 33 33 19 7 25
33 33 33 20 6 25
33 33 33 21 5 25
33 33 33 22 4 25
33 33 33 23 3 25
33 33 33 24 2 25
33 33 33 25 1 25
33 33 33 25 2 24
33 33 33 25 3 23
33 33 33 25 4 22
33 33 33 25 5 21
33 33 33 25 6 20
33 33 33 25 7 19
33 33 33 25 8 18
33 33 33 25 9 17
33 33 33 25 10 16
33 33 33 25 11 15
33 33 33 25 12 14
33 33 33 25 13 13
33 33 33 25 14 12
33 33 33 25 15 11
33 33 33 25 16 10
33 33 33 25 17 9
33 33 33 25 18 8
33 33 33 25 19 7
33 33 33 25 20 6
33 33 33 25 21 5
33 33 33 25 22 4
33 33 33 25 23 3
33 33 33 25 24 2
33 33 33 25 25 1
  • 打赏
  • 举报
回复
to #121
你把问题限定了,就是每个数都从1-33取值,这样就和原来的任意取值不符了。

另外,你的结果不全,口算就知道
6个25
1个27,2个24,3个25,都是结果。
wkklun22 2009-11-23
  • 打赏
  • 举报
回复
这个有难度呀,俺只会写33选6的代码
holsten32 2009-11-21
  • 打赏
  • 举报
回复
wekui 2009-11-21
  • 打赏
  • 举报
回复
han
木薯超人 2009-11-21
  • 打赏
  • 举报
回复
2楼正解
link_biao 2009-11-21
  • 打赏
  • 举报
回复
哎。
鸵鸟 2009-11-21
  • 打赏
  • 举报
回复
算法复杂度O(N^3)
大致思路

计算任意三个数字和 记为NUM(三个for 循环)
if NUM < 150
{
将NUM放到hash table中
}

CNT = 0
计算任意三个数字和 记为NUM(三个for 循环)
if NUM < 150
{
if (150 - NUM, 已经在hash table 中, 加上你自己的判断条件<排列?还是组合?>) ++CNT
}

return CNT
加载更多回复(112)

62,628

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧