首页 > 代码库 > 回溯法第5题—自然数的拆分问题

回溯法第5题—自然数的拆分问题

[问题描述]输入自然数n,然后将其拆分成由若干数相加的形式,参与加法运算的数可以重复。

输入:待拆分的自然数n。

输出:若干数的加法式子。

[样例输入]

7

[样例输出]

7=1+67=1+1+57=1+1+1+47=1+1+1+1+37=1+1+1+1+1+27=1+1+1+1+1+1+17=1+1+1+2+27=1+1+2+37=1+2+47=1+2+2+27=1+3+37=2+57=2+2+37=3+4

[问题分析]

很明显是数的组合题目,需满足两个条件:

(1)每一组数之和必须等于n;

(2)每一组数的个数是不确定的;

我们可以使等式中的后一个数的大小一定大于或等于前一个数,也就是不下降串,这样可以避免剪枝,提高效率。

//下面是我写的程序,代码很短,标程有一些细节没有考虑,因此标程暂不给出

var a,b:array [1..100]  of  byte;    n:integer;procedure try(m,start,k:integer);{探求第k个可取的数}{m表示待拆分的数值,start表示可拆分数的起始值}var i,j:integer;begin for i:=start to (m>>1) do{m>>1 == m div 2}  begin   a[k]:=m-i;b[k]:=i;{a数组表示待拆分的数,b数组表示参与拆分的数}   write(n,=);   for j:=1 to k do write(b[j],+);   writeln(a[k]);   try(a[k],i,k+1);  end;end;begin  readln(n);  try(n,1,1);end.

如果感到这道题有些难理解,我们可以一步一步的来执行:

(下面以n=7为例)

首先,我们try(n,1,1);

a[1]=6,b[1]=1;

然后我们就可以输出7=1+6;

然后try(6,1,2)对6进行拆分,a[2]=6-1=5,b[2]=1,于是又可以输出7=1+1+5;

接着try(5,1,3),a[3]=5-1=4,b[3]=1,于是有7=1+1+1+4;

try(4,1,4),得到a[4]=4-1=3,b[4]=1,输出7=1+1+1+1+3;

try(3,1,5),a[5]=3-1=2,b[5]=1,print(7=1+1+1+1+1+2);

try(2,1,6),a[6]=2-1=1,b[6]=1,print(7=1+1+1+1+1+1+1);

try(1,1,7),不满足循环条件,回溯

回到上一步,循环已经结束,回溯

回到上一步,循环已经结束,回溯

回到上一步,a[4]=4-2=2,b[2]=2,print(7=1+1+1+2+2)

try(2,2,5),不满足循环条件,回溯

回到上一步,a[3]=5-2=3,b[3]=2,print(7=1+1+2+3)

try(3,2,4),不满足循环条件,回溯

回到上一步,a[2]=6-2=4,b[2]=2,print(7=1+2+4)

try(4,2,3),满足循环条件,循环一次

a[3]=4-2=2,b[3]=2,print(7=1+2+2+2)

……………………………………

后面执行的就不写了,经过这一番执行,想必大家对这个问题都有了新的认识。

那么我这费大力气写出执行过程也就很有价值了。