请教一个算法,delphi6.0(100分)

  • 主题发起人 主题发起人 seafox
  • 开始时间 开始时间
S

seafox

Unregistered / Unconfirmed
GUEST, unregistred user!
请教有一个自然数列,例如1,2,3....30,从中选出N个数,并且N个数的和为M(M也为自然数)
请高手一回,谢谢!!
 
M,N有甚麼要求?
 
用冒泡法循环相加
 
穷举一把,虽然效率是最低的
注意只要穷举1~Min(30, M)即可。
 
用穷举法,当M大于30的时候需要计算的次数是C(n,30)(组合),当n=15时次数最大,算了一下大概
是155117520次,不会用太多时间。
 
把所给的n个自然数分为两组,一组为奇数,一组为偶数。
如果m为奇数,则从奇数组中取奇数个奇数(设为j),从偶数组中取n-j个偶数。
如果m为偶数,则有两种可能:1,n个数全取偶数;2,取偶数个奇数,剩下的取偶数。
在这个分类的情况下再用穷举法。去掉了一些不可能的情况,效率应该会高些,但根本
问题没解决,等高手来解决吧!
 
M>N
用回溯法,先取最大的。
 
up:
楼上的方法没错。
 
楼主的标题‘请教一个算法,delphi6.0’是什么意思?
难道算法是区分语言并且还区分版本的吗?
 
如果理解成以下的话,将是非常简单,不知理解是否有问题:
从 1到N 这N个数中取出某一些数,这些数的和为 M

function getSumItem(N,M,Index:integer;var Its:Array of integer):Integer;
begin
Result:=0;
//开始就 M 比 N 小,没有意义还用算吗,
// 1,2 不能分解,大于1的,1+(M-1)
if (Index=1) and (m<=n) then
begin
if M<3 then
exit;
Result:=2;
Its[1]:=1;
Its[2]:=M-1;
exit;
end;
//其实这种情况不可能,还是判断一下
if m<1 then
exit;
//全部加起来都不够,不用算了
if m>((n+1)*n div 2) then
exit;
if N>=m then
//和比最大项小,直接取这外和数就可以
begin
Its[Index]:=m;
Result:=Index;
end
else
begin
Its[Index]:=N;
//把最大项取出来,前面的和总能等于 M-N
Result:=getSumItem(N-1,m-N,Index+1,Its);
end;
end;

// 其实哪用得着递归
function getSumItemSimple(N,M:integer;var Its:Array of integer):Integer;
var
i,j,Sum:integer;
begin
Result:=0;
sum:=(((n+1)*n) shr 1);
if (m<=2) or (m>Sum) then
exit;
if m<=n then
begin
result:=2;
Its[1]:=1;
Its[2]:=M-1;
exit;
end;

Sum:=0;
j:=0;
for i:=ndo
wnto 1do
begin
inc(j);
if i<(m-sum) then
begin
Sum:=Sum+i;
Its[j]:=i;
end
else
begin
Its[j]:=M-Sum;
break;
end;
end;
Result:=j;
end;

const MaxN=1000;
function showSumItem(N,m:integer;Simple:Boolean):string;
var
x:array[0..MaxN] of integer;
i,K:integer;
s:string;
begin
for i:=1 to high(x)do
x:=0;
if simple then
k:=getSumItemSimple(n,m,x)
else
k:=getSumItem(n,m,1,x);
if k=0 then
s:='和太大,或和小于3。无解!'
else
begin
s:=Format('分解为 %d 项:%d',[k,x[1]]);
for i:=2 to kdo
s:=s+Format('+%d',[x]);
end;
Result:=s;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Showmessage(showSumItem(10,15,false));
Showmessage(showSumItem(10,15,true));
Showmessage(showSumItem(10,2,false));
Showmessage(showSumItem(10,2,true));
Showmessage(showSumItem(10,100,false));
Showmessage(showSumItem(10,100,true));
Showmessage(showSumItem(100,1000,false));
Showmessage(showSumItem(100,1000,true));
end;

 
先谢谢大家,我这个算珐的意思是从1,2,3....30(或变量X,X为大于30的整数)的自然数列中
从中拿出N个数(N为小于30或X的数)并且这N个自然数的和为M(M为整数,是计算前录入的变量),计算后把
这N个自然数列出来,结果不能重复。
列如:1,2,3...30中选出5(或变量N)个数,并且和为15(或变量M)的数的组合为1+2+3+4+5=15;[:)]
 
to jsxjd
谢谢你把源程序都写出来了,你可以改一下吗,从自然数列中选出x(x为整数,是计算前录入的变量)
个数,让他们的和等于m(M为整数,是计算前录入的变量)的组合,不能重复,先谢谢
 
向高手学习!
 
用枚举法恐怕行不能。
先 1...N 选入,然后根据差的情况,进行置换。
我考虑考虑。
 
function getNum(Maxn,n,m:integer):TStrings;
var
i,x,y,t2,t3:integer;
begin
Result:=TStringList.create;
result.Add('无解');
if (n>Maxn) or (n<1) then
exit;
x:=((n+1)*n) div 2;
y:=x+(maxn-n)*n;
if (m<x) or (m>y) then
exit;
Result.clear;
t2:=m-x;
t3:=t2 div n;
y:=t3;
t3:=t2 mod n;
for i:=1 to ndo
Result.Add(inttostr(i+y));
if t3<>0 then
begin
Result.Delete(N-t3);
Result.add(inttostr(y+n+1));
end;

end;

procedure TForm1.Button1Click(Sender: TObject);
begin
memo1.Lines.assign(getNum(30,5,101));
end;
 
分治+递归可能能提高效率。
先得用数学公式推导出分治后的原型,也就是一些数学运算。
 
我的思路是这样的,例如,你要在1-30序列中,找出这样一些序列,每个序列包含四个数,它们的和
等于21,可以这样做,先确定四个数中的最大值,用 21-(1+2+3)=15,(如果你所求的序列有5个数,就要
用21-(1+2+3+4)啦.这样先确定了一个最大数,再求其他三个较小数,先假定最大数是不是15吧,
那其他三个数的和是21-15,再确定三个数中的最大数,方法依旧,用6-(1+2)=3,得最大数3,
再求两个数中的最大数,用材林3-1=2,再得另一个数3-1=1;这样一个序列就求出来拉.然后再
做这样的循环,最大数减1,次大数加1,循环直到最大数等于次大数为止.例如:21=1+2+3+15;
21=1+2+4+14;21+1+2+5+13;.......21=1+2+10+9.大概就是这样了,可用递归和循环实现.
 
接受答案了.
 
后退
顶部