数字组合问题

java1109 2009-09-25 11:18:13
1到19的数字 任选几个数让这几个数的和为20
列出所有的可能
ps:数字不能重复 1 + 1 + 18 是不合法的
1 + 19 和 19 + 1 记为一种情况
...全文
227 13 打赏 收藏 转发到动态 举报
写回复
用AI写文章
13 条回复
切换为时间正序
请发表友善的回复…
发表回复
Smile_Tiger 2009-09-29
  • 打赏
  • 举报
回复
可以这样逐步划分问题来考虑

1。 先考虑两个数相加的结果,一共有A0 ... An1种
2。 然后考虑两个数相加的结果分别为A0、A1...An1的各个情况B0 ... Bn2种
3。 以此类推到不能分解为止
4。 以上结果形成了一颗树
5。 根据树来统计、遍历所有的情况,每种情况都是从树叶到树根的路径
PeacefulBY 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 linren 的回复:]
引用 3 楼 peacefulby 的回复:
穷举的方法不能扩展啊,用动规吧,设number(20,19)表示组成20,且最大数为19的方案,这样每次考虑真正的最大数即可,伪码:
number(int sum, int max)
{
  各种出口条件,例如sum = 20, max = 1这就不可能有方案了
  for i = 2 to max
    递归number(sum-i, i-1)
}

穷举的效率太低了
10楼的程序找size=40的情况就有些吃力了……
不知道有没有可以利用动态规划打印所有的解的办法……
[/Quote]
记忆化搜索,或者从底层开始构建,不过后者比较难……
linren 2009-09-27
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 peacefulby 的回复:]
穷举的方法不能扩展啊,用动规吧,设number(20,19)表示组成20,且最大数为19的方案,这样每次考虑真正的最大数即可,伪码:
number(int sum, int max)
{
各种出口条件,例如sum = 20, max = 1这就不可能有方案了
for i = 2 to max
递归number(sum-i, i-1)
}
[/Quote]嗯
穷举的效率太低了
10楼的程序找size=40的情况就有些吃力了……
不知道有没有可以利用动态规划打印所有的解的办法……
linren 2009-09-27
  • 打赏
  • 举报
回复
【程序】
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct level{
int *now;
int len;
};

void add(struct level *llv,int n){
int i=0;
llv->len=n;
llv->now=(int*)malloc(sizeof(int)*n);
memset((char*)(llv->now),0,sizeof(int)*n);
for(i=0;i<n;i++){
llv->now[i]=i;
}
}

void del(struct level *llv){
free(llv->now);
}

void fun(int size){
int i,j,k,n,z,t;
struct level p;
n=size;
i=1;
while(i*(i+1)/2<=n) i++;
i--;
if(i<=1){
printf("no answer!\n");return;
}
n=i;

for(t=2;t<=n;t++){
add(&p,t);
while(1){
z=0;
for(i=0;i<t;i++){
z+=p.now[i]+1;
}
if(z==size){
for(i=0;i<t;i++) printf("%d, ",p.now[i]+1);
printf("\n");
}
if(p.now[p.len-1]==size-1){
for(i=1,j=p.len-2;j>=0;j--,i++){
if((p.now[j])<(size-1-i)) break;
}
if(j<0) break;
p.now[j]++;
k=p.now[j]+1;
for(i=j+1;i<p.len;i++) p.now[i]=k++;
}else{
p.now[p.len-1]++;
}
}
del(&p);
}
}

int main(){
int i;
for(i=1;i<=7;i++){
printf("\n【n=%d】\n",i);
fun(i);
}

printf("\n【n=20】\n");
fun(20);
return 0;
}

【运行结果】

【n=1】
no answer!

【n=2】
no answer!

【n=3】
1, 2,

【n=4】
1, 3,

【n=5】
1, 4,
2, 3,

【n=6】
1, 5,
2, 4,
1, 2, 3,

【n=7】
1, 6,
2, 5,
3, 4,
1, 2, 4,

【n=20】
1, 19,
2, 18,
3, 17,
4, 16,
5, 15,
6, 14,
7, 13,
8, 12,
9, 11,
1, 2, 17,
1, 3, 16,
1, 4, 15,
1, 5, 14,
1, 6, 13,
1, 7, 12,
1, 8, 11,
1, 9, 10,
2, 3, 15,
2, 4, 14,
2, 5, 13,
2, 6, 12,
2, 7, 11,
2, 8, 10,
3, 4, 13,
3, 5, 12,
3, 6, 11,
3, 7, 10,
3, 8, 9,
4, 5, 11,
4, 6, 10,
4, 7, 9,
5, 6, 9,
5, 7, 8,
1, 2, 3, 14,
1, 2, 4, 13,
1, 2, 5, 12,
1, 2, 6, 11,
1, 2, 7, 10,
1, 2, 8, 9,
1, 3, 4, 12,
1, 3, 5, 11,
1, 3, 6, 10,
1, 3, 7, 9,
1, 4, 5, 10,
1, 4, 6, 9,
1, 4, 7, 8,
1, 5, 6, 8,
2, 3, 4, 11,
2, 3, 5, 10,
2, 3, 6, 9,
2, 3, 7, 8,
2, 4, 5, 9,
2, 4, 6, 8,
2, 5, 6, 7,
3, 4, 5, 8,
3, 4, 6, 7,
1, 2, 3, 4, 10,
1, 2, 3, 5, 9,
1, 2, 3, 6, 8,
1, 2, 4, 5, 8,
1, 2, 4, 6, 7,
1, 3, 4, 5, 7,
2, 3, 4, 5, 6,
Press any key to continue

【说明】
函数fun(int size)的作用:

从1到(size-1)里面任选几个的不重复数字
列出所有满足几个数的和为size的情况……
绿色夹克衫 2009-09-27
  • 打赏
  • 举报
回复
用hash或数组记录中间已经算出的结果就可以了!

[Quote=引用 7 楼 sytstarac 的回复:]
不知道这个算不算动态规划,感觉性能好差,那位帮优化一下。
[/Quote]
ahjoe 2009-09-27
  • 打赏
  • 举报
回复
优化一:不用数组元素做循环控制变量

优化二:若数之和大于20,结束最后一层循环
linren 2009-09-26
  • 打赏
  • 举报
回复
【程序】
#include <stdio.h>

int main(){
int p[5];
//2
for(p[0]=1;p[0]<=19;p[0]++){
for(p[1]=p[0]+1;p[1]<=19;p[1]++){
if(p[0]+p[1]==20) printf("%d, %d\n",p[0],p[1]);
}
}
//3
for(p[0]=1;p[0]<=19;p[0]++){
for(p[1]=p[0]+1;p[1]<=19;p[1]++){
for(p[2]=p[1]+1;p[2]<=19;p[2]++){
if(p[0]+p[1]+p[2]==20) printf("%d, %d, %d\n",p[0],p[1],p[2]);
}
}
}
//4
for(p[0]=1;p[0]<=19;p[0]++){
for(p[1]=p[0]+1;p[1]<=19;p[1]++){
for(p[2]=p[1]+1;p[2]<=19;p[2]++){
for(p[3]=p[2]+1;p[3]<=19;p[3]++){
if(p[0]+p[1]+p[2]+p[3]==20){
printf("%d, %d, %d, %d\n",p[0],p[1],p[2],p[3]);
}
}
}
}
}
//5
for(p[0]=1;p[0]<=19;p[0]++){
for(p[1]=p[0]+1;p[1]<=19;p[1]++){
for(p[2]=p[1]+1;p[2]<=19;p[2]++){
for(p[3]=p[2]+1;p[3]<=19;p[3]++){
for(p[4]=p[3]+1;p[4]<=19;p[4]++){
if(p[0]+p[1]+p[2]+p[3]+p[4]==20){
printf("%d, %d, %d, %d, %d\n",p[0],p[1],p[2],p[3],p[4]);
}
}
}
}
}
}
return 0;
}

【运行结果】
1, 19
2, 18
3, 17
4, 16
5, 15
6, 14
7, 13
8, 12
9, 11
1, 2, 17
1, 3, 16
1, 4, 15
1, 5, 14
1, 6, 13
1, 7, 12
1, 8, 11
1, 9, 10
2, 3, 15
2, 4, 14
2, 5, 13
2, 6, 12
2, 7, 11
2, 8, 10
3, 4, 13
3, 5, 12
3, 6, 11
3, 7, 10
3, 8, 9
4, 5, 11
4, 6, 10
4, 7, 9
5, 6, 9
5, 7, 8
1, 2, 3, 14
1, 2, 4, 13
1, 2, 5, 12
1, 2, 6, 11
1, 2, 7, 10
1, 2, 8, 9
1, 3, 4, 12
1, 3, 5, 11
1, 3, 6, 10
1, 3, 7, 9
1, 4, 5, 10
1, 4, 6, 9
1, 4, 7, 8
1, 5, 6, 8
2, 3, 4, 11
2, 3, 5, 10
2, 3, 6, 9
2, 3, 7, 8
2, 4, 5, 9
2, 4, 6, 8
2, 5, 6, 7
3, 4, 5, 8
3, 4, 6, 7
1, 2, 3, 4, 10
1, 2, 3, 5, 9
1, 2, 3, 6, 8
1, 2, 4, 5, 8
1, 2, 4, 6, 7
1, 3, 4, 5, 7
2, 3, 4, 5, 6
Press any key to continue

【说明】
只用一个在1~19内的数是无法构成20的……
考虑用六个互不相同数相加最小的值为1+2+3+4+5+6=21>20

因此只用查找2个、3个、...、5个数构成的情况……
sytstarac 2009-09-26
  • 打赏
  • 举报
回复
不知道这个算不算动态规划,感觉性能好差,那位帮优化一下。
sytstarac 2009-09-26
  • 打赏
  • 举报
回复

#include <stdio.h>
#include <stdlib.h>
const int gsum=20;
const int gmax=10;

int howmany(int sum,int max)
{
if(sum==0||max==0)return 0;
if(sum==1)return 1;
if(sum>max)
return howmany(sum,max-1)+howmany(sum-max,max-1);
else if(sum==max)
return howmany(sum,max-1)+1;
else
return howmany(sum,max-1);
}
int main()
{
int total=howmany(gsum,gmax);
printf("%d\n",total);
return 0;
}
绿色夹克衫 2009-09-26
  • 打赏
  • 举报
回复
只求个数的话,以前写过一个!和这个问题应该是一样的。

http://acm.timus.ru/problem.aspx?space=1&num=1017


using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Dictionary<int, long> HashSet = new Dictionary<int, long>();

int N = int.Parse(Console.ReadLine());

long count = Staircases(1, N, N + 1, HashSet);
Console.WriteLine(count - 1);
}

static long Staircases(int min, int remain, int total, Dictionary<int, long> hash)
{
if (min > remain)
return 0;
if (min == remain)
return 1;

int hashcode = min * total + remain;

if (hash.ContainsKey(hashcode))
return hash[hashcode];

long totalCount = 1;

for (int i = min; i < (remain + 1) >> 1; i++)
{
totalCount += Staircases(i + 1, remain - i, total, hash);
}

hash.Add(hashcode, totalCount);
return totalCount;
}
}
}

  • 打赏
  • 举报
回复
有好算法吗?
PeacefulBY 2009-09-26
  • 打赏
  • 举报
回复
[Quote=引用 2 楼 linren 的回复:]
【程序】
C/C++ code#include<stdio.h>int main(){int p[5];//2for(p[0]=1;p[0]<=19;p[0]++){for(p[1]=p[0]+1;p[1]<=19;p[1]++){if(p[0]+p[1]==20) printf("%d, %d\n",p[0],p[1]);
}
}//3for(p[0]=1;p[0]<=19;p[0]++){for(p[1]=p[0]+1;p[1]<=19;p[1]++){for(p[2]=p[1]+1;p[2]<=19;p[2]++){if(p[0]+p[1]+p[2]==20) printf("%d, %d, %d\n",p[0],p[1],p[2]);
}
}
}//4for(p[0]=1;p[0]<=19;p[0]++){for(p[1]=p[0]+1;p[1]<=19;p[1]++){for(p[2]=p[1]+1;p[2]<=19;p[2]++){for(p[3]=p[2]+1;p[3]<=19;p[3]++){if(p[0]+p[1]+p[2]+p[3]==20){
printf("%d, %d, %d, %d\n",p[0],p[1],p[2],p[3]);
}
}
}
}
}//5for(p[0]=1;p[0]<=19;p[0]++){for(p[1]=p[0]+1;p[1]<=19;p[1]++){for(p[2]=p[1]+1;p[2]<=19;p[2]++){for(p[3]=p[2]+1;p[3]<=19;p[3]++){for(p[4]=p[3]+1;p[4]<=19;p[4]++){if(p[0]+p[1]+p[2]+p[3]+p[4]==20){
printf("%d, %d, %d, %d, %d\n",p[0],p[1],p[2],p[3],p[4]);
}
}
}
}
}
}return0;
}
【运行结果】
Assembly code1,192,183,174,165,156,147,138,129,111,2,171,3,161,4,151,5,141,6,131,7,121,8,111,9,102,3,152,4,142,5,132,6,122,7,112,8,103,4,133,5,123,6,113,7,103,8,94,5,114,6,104,7,95,6,95,7,81,2,3,141,2,4,131,2,5,121,2,6,111,2,7,101,2,8,91,3,4,121,3,5,111,3,6,101,3,7,91,4,5,101,4,6,91,4,7,81,5,6,82,3,4,112,3,5,102,3,6,92,3,7,82,4,5,92,4,6,82,5,6,73,4,5,83,4,6,71,2,3,4,101,2,3,5,91,2,3,6,81,2,4,5,81,2,4,6,71,3,4,5,72,3,4,5,6
Press any key to continue
【说明】
只用一个在1~19内的数是无法构成20的……
考虑用六个互不相同数相加最小的值为1+2+3+4+5+6=21>20

因此只用查找2个、3个、...、5个数构成的情况……
[/Quote]
穷举的方法不能扩展啊,用动规吧,设number(20,19)表示组成20,且最大数为19的方案,这样每次考虑真正的最大数即可,伪码:
number(int sum, int max)
{
各种出口条件,例如sum = 20, max = 1这就不可能有方案了
for i = 2 to max
递归number(sum-i, i-1)
}
acdbxzyw 2009-09-25
  • 打赏
  • 举报
回复
幫頂。
測試頭像。

33,028

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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