注意到起始长度是 5 ,之后每次 n 增加 1 ,两边各增大 2 ,所以总长度为 5+4n ,因此数组每个维度范围不应该小于 5+4\times30 ,且一开始可以直接先铺满这个区域全部是 .
,之后每次画用 $
替代 .
即可。
使用递归来做的话, 可以设当前区域左上角下标为 (x,y) ,横坐标从上往下,从坐标从左往右。观察可知,除了最中心的小十字之外,其他每一层的大十字都很有规律 (其实中间的十字也可以用相同的规律生成)。假设先不考虑中心小十字:
n \ge 1 时,当前区域跨度是 5+4n ,可以以 (x,y) 为左上角,画长为 1+4n 的四条直线和四个内折的角(转化为八条直线)。设 l=4n
则这么一个大十字可具体为:
如图所示:
可以设一个绘制直线的函数,然后每次画第 i 个大十字复用 12 次,以减轻工作量。
那么,设 f(n) 是绘制第 n 个大十字,有: f(n)=f(n-1)+十二条直线 特别地,根据上面的十二条直线,我们发现若 n=0,l=1 ,恰能对应最中心的小十字。由此,可得 f 定义域为 n\ge 0 。
绘制直线函数可以抽象为画矩形函数(长或宽为 1) ,从而避免横竖的分类讨论。
绘制直线和递归函数的具体实现参见代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[131][131];
void line(ll ax, ll ay, ll bx, ll by)
{
for (ll i = ax; i <= bx; ++i)
{
for (ll j = ay; j <= by; ++j)
{
a[i][j] = '$';
}
}
}
void draw(ll x, ll y, ll l, ll n)
{
if (n < 0)
{
return;
}
line(x + 2, y, x + 2 + l, y);
line(x, y + 2, x, y + 2 + l);
line(x + 2, y + 4 + l, x + 2 + l, y + 4 + l);
line(x + 4 + l, y + 2, x + 4 + l, y + 2 + l);
line(x + 2, y, x + 2, y + 2);
line(x, y + 2, x + 2, y + 2);
line(x, y + 2 + l, x + 2, y + 2 + l);
line(x + 2, y + 2 + l, x + 2, y + 4 + l);
line(x + 2 + l, y, x + 2 + l, y + 2);
line(x + 2 + l, y + 2, x + 4 + l, y + 2);
line(x + 2 + l, y + 2 + l, x + 4 + l, y + 2 + l);
line(x + 2 + l, y + 2 + l, x + 2 + l, y + 4 + l);
draw(x + 2, y + 2, l - 4, n - 1);
}
ll n, l;
signed main()
{
cin >> n;
l = 5 + 4 * n;
for (ll i = 0; i < l; ++i)
{
for (ll j = 0; j < l; ++j)
{
a[i][j] = '.';
}
}
draw(0, 0, 4 * n, n);
for (ll i = 0; i < l; ++i)
{
puts(a[i]);
}
return 0;
}