算法笔记 第四章-算法初步 | 4.3递归——谢尔宾斯基地毯、自然数分解之最大积、自然数分解之方案数、01串
谢尔宾斯基地毯
题目描述:

题目链接:
谢尔宾斯基地毯
解题思路:
和盒分形的做法类似,用一个二维数组打印图形,注意二维数组要为外圈的”+”留位置。
具体的递归就依照图中所画规律实现即可,递归的出口是n=1。
做题过程:
打印的时候总是错误,一步步排查发现是在输入’X’的时候j的初始值赋了x+len,改为y+len就顺利通过了。
#include
#include
//n最大为7,所以边长最长为3^6+2(2是给'+'留下的位置)
#define MAX 3*3*3*3*3*3+2
char blanket[MAX][MAX];
//n是递归层数,x、y是左上角坐标
void BLANKET(int n,int x,int y){
//递归出口
if(n==1){
blanket[x][y]=' ';
}else {
//F(n-1)的边长是3的n-2次方
int len=(int)pow(3,n-2);
//左上
BLANKET(n-1,x,y);
//上
BLANKET(n-1,x+len,y);
//右上
BLANKET(n-1,x+2*len,y);
//左
BLANKET(n-1,x,y+len);
//中
for(int i=x+len;i<x+2*len;i++){
for(int j=y+len;j<y+2*len;j++){
blanket[i][j]='X';
}
}
//右
BLANKET(n-1,x+2*len,y+len);
//左下
BLANKET(n-1,x,y+2*len);
//下
BLANKET(n-1,x+len,y+2*len);
//右下
BLANKET(n-1,x+2*len,y+2*len);
}
}
int main(){
int n;
scanf("%d",&n);
//地毯从(1.1)开始,因为外圈还要放'+'
BLANKET(n,1,1);
//F(n)的边长
int k=(int)pow(3,n-1);
//绘制外圈的'+'
for(int j=0;j<k+2;j++){
blanket[0][j]='+';
}
for(int i=0;i<k+2;i++){
blanket[i][0]='+';
}
for(int i=0;i<(int)pow(3,n-1)+2;i++){
blanket[i][k+1]='+';
}
for(int j=0;j<k+2;j++){
blanket[k+1][j]='+';
}
//输出图形
for(int i=0;i<k+2;i++){
for(int j=0;j<k+2;j++){
printf("%c",blanket[i][j]);
}
printf("\n");
}
return 0;
}
归纳总结:
题解中没有把’+’写入二维数组,而是在需要的地方直接打印出来。我的做法是写入数组,这样递归的(x,y)从(1,1)出发,更方便我计算坐标。
自然数分解之最大积
题目描述:

题目链接:
自然数分解之最大积
解题思路:
先找积最大时的规律:n=2时,最大是1*1=1;n=3时,最大是1*2=2;n=4时,最大是2*2=4;n=5时,最大是2*3=6;n=6时,最大是3*3=9;n=7时,最大是3*2*2=12;n=8时,最大3*3*2=18…
可见在n大于3的时候应该优先分解出来3,不够的情况下分解出2,不要出现1。但是当n小于等于3的时候,不可避免要分解出来1,所以我对第一次进入递归时候的n进行了判断,如果n小于等于3,单独处理。
做题过程:
#include
int first=1;//first=1表示第一次
int max_mul(int n){
if(first==1){//如果是第一次,单独处理
if(n<3)
return 1;
if(n==3)
return 2;
first=0;//first=0,表示不是第一次
}
if(n<=3)//后面n小于等于3,直接返回n值即可,比如6递归调用了3*max_mul(3)
return n;
else{
if(n%3==0){//如果能除尽3,就只分解为3的乘积
return 3*max_mul(n-3);
}else{
return 2*max_mul(n-2);//如果不能除尽3,就分解出来一个2
}
}
}
int main(){
int n;
scanf("%d",&n);
printf("%d",max_mul(n));
return 0;
}
归纳总结:
本来想的做法是将n对半分,两边各调用一次递归,但是发现这样做的乘积不是最大的。
题解的代码没有注释也没写思路,没有看明白。我参考了其他人的非递归代码的思路写了这个代码,和题解的做法比起来,单独处理第一次的情况的代码确实丑陋了一点。
自然数分解之方案数
题目描述:

题目链接:
自然数分解之方案数
解题思路:
总结分解的规律,见下图:
2=1+1 3=1+1+1 3=1+2 4=1+1+1+1 4=1+1+2 4=1+3 4=2+2 5=1+1+1+1+1 5=1+1+1+2 5=1+1+3 5=1+2+2 5=1+4 5=2+3 6=1+1+1+1+1+1 6=1+1+1+1+2 6=1+1+1+3 6=1+1+2+2 6=1+1+4 6=1+2+3 6=2+2+2 6=1+5 6=2+4 6=3+3
可以看到第一步是从n个1开始划分,然后n的数量逐渐减少,n减少时去掉的值要分配到前面的数中,但是要注意始终要保持图中类似单调不减的关系才能确保不重复,n=1的时候退出递归。
做题过程:
#include
//n是自然数,bound是上界
int f(int n, int bound) {
//当n小于等于1或者上界小于等于0时,无法分解
if (n <= 1 || bound <= 0) {
return 0;
} else {
//记录方案数
int ans = 0;
//i从1到上界
for (int i = 1; i 0 && n - i <= i && n - i <= bound) {
ans++;
}
}
//向上层返回方案数
return ans;
}
}
int main() {
int n;
scanf("%d", &n);
//至少要有两个加数,所以其中一个加数的初值最大是n-1
printf("%d\n", f(n, n - 1));
return 0;
}
归纳总结:
这题没太搞懂,看了一下提交数,果然提交的人也很少,只能看着题解来做题了。在网上查找资料,这是一种比较经典的DFS(深度优先遍历)例题,本质上DFS也是用递归实现的,但是还是只能说得多学,希望后面学到DFS章节,对这些内容的理解能深刻点。
01串
题目描述:
在一个长度为n的数组中填写0或者1,输出所有可能的结果。
题目链接:
01串
解题思路:
和上面的题目类似,有深度优先遍历的感觉。
用一个大小为n的数组存放所有数据,每层递归先将0放入数组末尾,当到达递归最底层时,输出数组的所有元素。然后返回上一层,返回后删掉最后一个0,将1放在数组末尾,然后进入下一层输出数组的所有元素。在从下一层返回,返回后删掉最后一个1,此时可以返回上上一层了。
做题过程:
#include
#include
using namespace std;
//存放所有字符
vector vec;
//n是字符串长度
void zero_one(int n){
//n=0时到达字符串末尾,可以输出全部元素
if(n==0){
for(int i=0;i<vec.size();i++){
cout<<vec[i];
}
cout<>n;
zero_one(n);
return 0;
}
归纳总结:
感觉和上题很类似,但是这题我倒是自己写出来了,大概因为这个的遍历过程很符合人的直觉,
一开始漏掉了最后一句vec.pop_back(),输出的字符参差不齐,还以为自己的思路错误了,还好后面补上后提交顺利通过。
本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/22ad4fa6b6.html
