首页 > 代码库 > 回溯法第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)
……………………………………
后面执行的就不写了,经过这一番执行,想必大家对这个问题都有了新的认识。
那么我这费大力气写出执行过程也就很有价值了。