ybt1318 自然数拆分
【题目描述】
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。
当n=7共14种拆分方法:
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
total=14
【输入】
输入n。
【输出】
按字典序输出具体的方案。
【输入样例】
7
【输出样例】
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
【题解】
观察样例,可以看出每个方案的加数是升序排列的
在一开始,我把这道题想成ybt1200 分解因数的加法版本,但是分解因数只是要求输出方案数,没法输出方案,而且方案数累加过程中,是不按字典序递增的,同样不行。
一开始走的弯路:
如果用分解不行,为什么不把n先看做n个1,然后合并呢?(这只是个猜想,因为没看题解,只能走一步说一步)
但是我想了想就放弃了,毕竟这个单元是搜索嘛!
正解
后来我就又想到可以借鉴爬楼梯的思路(爬楼梯是按照最后一步迈几级分类),把情况按照当前的第一个加数(最小的)分类:先讨论第一个加数是1的情况,全部输出后在讨论第一个加数是2的情况······以此类推,最后一直到第一加数是n/2(向下取整)(因为加数最少两个,而每个加数递增排列,所以最小加数最大为n/2)
这样,就这问题分解了。最小加数为1时,输出1,然后按n=n-1的情况处理。
边界条件就是当n=1时,分解后就是1。
打了一会代码,发现越来越迷茫,一看书上的题解,发现算法没毛病,只是输出有问题,那就继续自己打!
#include<iostream>
using namespace std;
int n,ans[10005];
void dfs(int x,int y) {//x表示还没有分配的n的数字,y表示当前所决定的最小加数位置
if(x==1){//只剩一了,不能分解,直接输出(当然,这种情况一个测试点只会执行一次,因为这里1是最后一个加数也就是最大的加数,这种情况只有n个加数均为1才会执行)
ans[y]=1;//将1作为当前最小加数也是最后一个加数
cout<<n<<"="<<ans[1];//输出开头和第一个加数
for(int k=2; k<=y ;k++) {
cout<<"+"<<ans[k];//输出后面的加数
}
cout<<endl;//方案输出完毕
}
else{
for(int j=ans[y-1];j<=x/2;j++) {
ans[y]=j;//给当前位赋值
dfs(x-j,y+1);//再分别以各个j为第y
}
ans[y]=x;//也可以选择不分解直接以第y位的x为最后一位
cout<<n<<"="<<ans[1];//输出
for(int k=2;k<=y;k++) {
cout<<"+"<<ans[k];
}
cout<<endl;
}
return;
}
int main() {
cin>>n;
for(int i=1;i<=n/2;i++) {//枚举不同的i,也就是首位最小加数
ans[1]=i;//首位赋值
dfs(n-i,2);//讨论后面的位
}
return 0;
}
这题也是一道dfs,做题时要注意按照输出顺序来决定算法。