题目描述
自从到了南蛮之地,孔明不仅把孟获收拾的服服帖帖,而且还发现了不少少数民族的智慧,他发现少数民族的图腾往往有着一种分形的效果,在得到了酋长的传授后,孔明掌握了不少绘图技术,但唯独不会画他们的图腾,于是他找上了你的爷爷的爷爷的爷爷的爷爷……帮忙,作为一个好孙子的孙子的孙子的孙子……你能做到吗?
输入格式
每个数据一个数字,表示图腾的大小(此大小非彼大小) n<=10
输出格式
这个大小的图腾
输入输出样例
输入 #1复制
2
输出 #1复制
/\ /__\ /\ /\ /__\/__\
输入 #2复制
3
输出 #2复制
/\ /__\ /\ /\ /__\/__\ /\ /\ /__\ /__\ /\ /\ /\ /\ /__\/__\/__\/__\
感想:想写出来很简单但是很难理解过程的题。我看过的好多书在递归这一章都会有一句这样的话——不要试图去理解递归,只需要知道这样做计算机就能帮你解决问题。想要理解递归确实很困难,但是作为一个目前还没学数据结构的铁头娃还是花了很多时间在这上面(虽然还是没能完全搞懂)就拿这题来记录一下我目前的理解吧。如果有大佬可以指出我的错误或者指导一下我就更好了www。。
分析:这题就是非常典型的分形分治。通过样例我们可以看到整个大三角可以分为上,左下,右下三个更小的三角形。从这里入手开始把解决大三角是通过递归一步步的化为解决更小的三角形来解决问题。但麻烦的是这个图型是由三个小三角形合成的且每个小三角形还得按照某种可以列出数学公式的规则摆放。主要麻烦就在这里。所以想到先用字符数组存储,格式,然后在总的输出。
#include <iostream>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
char arr[1051][2051];
int a;
int k = pow((double)2, a);
void dfs(int n, int x, int y) {
if (n == 1) {
arr[x][y] = '/';
arr[x][y + 1] = '\\';//注意反斜线把转义字符转义成常规字符
arr[x + 1][y - 1] = '/';
arr[x + 1][y + 2] = '\\';
arr[x + 1][y + 1] = '_';
arr[x + 1][y] = '_';
return;
}
dfs(n - 1, x, y);//构造上方的三角形
dfs(n - 1, x + pow(2, n - 1), y - pow(2, n - 1));//左下的,
//这些数据都可以通过数学归纳出来,这里不详细说,这里主要讨论递归过程
dfs(n - 1, x + pow(2, n - 1), y + pow(2, n - 1));//右下的
}
int main()
{
cin >> a;
for (int j = 1; j <= pow(2, a); j++)//初始化字符数组
for (int i = 1; i <= pow(2, a + 1); i++)
arr[j][i] = ' ';
dfs(a, 1, pow(2, a));//从中心点最上方的那个点开始
for (int j = 1; j <= pow(2, a); j++) {//输出数组
for (int i = 1; i <= pow(2, a + 1); i++) {
cout << arr[j][i];
}
cout << endl;
}
return 0;
}
从代码可以看出,主要思路是把大图像分为三个小部分递归解决。当递到最小一层即1的时候开始返回。如果学了数据结构的话应该会更好理解为什么只返回最小1层图像可以把整个大图像构建出来。因为这和正常的递推和递归不太一样,他不是从初始值开始按照某种数学规则递推下来的。而是通过每每个最小单元一步一步构建出来 的,过程类似搭积木。接下来我用光标定位函数和Sleep函数为大家演示其构建过程。(演示把输出规则改为了函数内部输出,以真实反映图像是怎么生成的。)
可以看到,先打印出了最上方的基本单元
这里搭建的顺序其实是我们的函数里哪个排前面的顺序。
这几张就更直观一点,从一个一个基本单元按照我们之前设定的数学公式从对应坐标开始按递归顺序一个一个构建大图像。
这里是演示用的源码。
#include <iostream>
#include<Windows.h>
#include<ctime>
#include<math.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
void gotoxy(short x, short y) {//光标定位
COORD pos = { x,y };
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);// 获取标准输出设备句柄
SetConsoleCursorPosition(hOut, pos);//两个参数分别是指定哪个窗体,具体位置
}
char arr[1051][2051];
int a;
int k = pow((double)2, a);
void dfs(int n, int x, int y) {
if (n == 1) {//分到最小的情况下开始归
arr[x][y] = '/';
gotoxy(y, x); cout << "/";
Sleep(100);
arr[x][y + 1] = '\\';//反斜线把转义字符转义成常规字符(有些拗口)
gotoxy(y+1,x); cout << "\\";
Sleep(100);
arr[x + 1][y - 1] = '/';
gotoxy(y-1, x+1); cout << "/";
Sleep(100);
arr[x + 1][y + 2] = '\\';
gotoxy(y+2, x+1); cout << "\\";
Sleep(100);
arr[x + 1][y + 1] = '_';
gotoxy(y + 1, x+ 1); cout << "_";
Sleep(100);
arr[x + 1][y] = '_';
gotoxy(y, x+1); cout << "_";
Sleep(100);
return;
}
dfs(n - 1, x, y);//构造正上方的三角
dfs(n - 1, x + pow(2, n - 1), y - pow(2, n - 1));//左下方的
dfs(n - 1, x + pow(2, n - 1), y + pow(2, n - 1));//右下方的
}
int main()
{
cin >> a;
for (int j = 1; j <= pow(2, a); j++)//初始化字符数组
for (int i = 1; i <= pow(2, a + 1); i++)
arr[j][i] = ' ';
dfs(a, 1, pow(2, a));//从中心
return 0;
}