挺有意思的一道大模拟,我用的方法似乎也比较特殊。那,发篇题解吧(虽然审核通道关了
事实上不太难。
我们知道,深度为 x x x 的满二叉树,其节点数为 2 x − 1 2^x-1 2x−1。因此,我们可以作出这样的预处理:
for(int i=1;i<(1<<m);i++)
{
a[i]=1;//a_i 表示编号为 i 的节点是否删除
}
同时,我们发现,如果一个节点删除了,那么其儿子也都会删除。所以,我们可以用一个递归来删除节点:
void del(int x)
{
a[x<<1]=0;
a[x<<1|1]=0;//删除
if(x<=(1<<m-1))//边界条件
{//递归删除
del(x<<1);
del(x<<1|1);
}
}
可以看出,如果我们从根节点开始绘图,会很麻烦。因此,我们可以从叶子节点开始画。
那么整个输入的过程就搞定了:
cin>>m>>n;
for(int i=1;i<(1<<m);i++)
{
a[i]=1;
}
while(n--)
{
int i,j;
cin>>i>>j;
int k=(1<<i-1)+j-1;
a[k]=0;
a[k<<1]=0;
a[k<<1|1]=0;
del(k<<1);
del(k<<1|1);
}
接下来就是最重要的一环了。
当然肯定要递归绘画,因此用函数 sol()
来绘制(我们先画原始的满二叉树)。
我们用 a n s i , j ans_{i,j} ansi,j 来存储结果。
首先,是绘制前的空格。可以发现,空格个数从下到上是递增的。因此我们可以用一个变量 d e p dep dep 来表示绘制的层数(也就是行数),不仅可以绘制空格,最后输出时也很有用。
同时,我们用 h h h 来表示当前绘制列数。
那就可以这样写:
int h=0;
for(int i=1;i<dep;i++)
{
ans[dep][h+i]=' ';
}
h+=dep;
接下来我们来说说 sol()
的参数。
我们用整数 x x x 表示当前绘制的节点数(也就是 o
的个数),用 s p _ l r sp\_lr sp_lr 表示同一个子树中左儿子和右儿子间的空格数(初始为 3 3 3),用 s p _ r l sp\_rl sp_rl 表示左子树的右儿子、右子树的左儿子间的空格数(初始为 1 1 1)。注意到后面时,会保证 s p _ l r = s p _ r l sp\_lr=sp\_rl sp_lr=sp_rl。
然后我们分两步:画节点和画连接线。
先是画节点(比较简单,不多说):
for(int i=1;i<=x;i++)
{
ans[dep][h]='o';//左儿子
for(int j=1;j<=sp_lr;j++)
{
ans[dep][h+j]=' ';
}//左儿子与右儿子间的空格数
ans[dep][h+sp_lr+1]='o';//右儿子
for(int j=1;j<=sp_rl;j++)
{
ans[dep][h+sp_lr+1+j]=' ';
}//左子树右儿子与右子树左儿子间的空格数
h+=sp_lr+sp_rl+2;
}
然后是画连接线。
画连接线时要注意, d e p dep dep 要加一, h h h 要归零。
而且,连接线间的空格一开始为 s p _ l r − 1 sp\_lr-1 sp_lr−1,随后逐渐 − 2 -2 −2,直到 = 1 =1 =1。
因此代码如下:
for(int i=sp_lr-2;i>=1;i-=2)//间距逐渐缩小
{
for(int j=1;j<=x;j++)//x 对连接线
{
for(int k=1;k<dep;k++)
{
ans[dep][h+k]=' ';
}
h+=dep;
ans[dep][h]='/';
for(int k=1;k<=i;k++)
{
ans[dep][h+k]=' ';
}
h+=i+1;
ans[dep][h]='\\';
for(int k=1;k<=dep;k++)
{
ans[dep][h+k]=' ';
}
h+=dep;
}
dep++;
h=0;//每次操作完 dep 都要加一,h 都要归零
}
每次做完这两样操作,就可以继续递归了:
sol(x>>1,sp_lr==3?5:(sp_lr+1<<1)-1,sp_lr==3?5:(sp_lr+1<<1)-1);
//x 每次除以二;sp_lr 随后与 sp_rl 相等
而边界,则是 x = 0 x=0 x=0 的时候:
if(x==0)
{
ans[dep][h]='o';//绘制根节点
return;
}
而一开始 x x x 的值,便是 2 m − 1 2 = 2 m − 2 \frac{2^{m-1}}{2}=2^{m-2} 22m−1=2m−2。
接下来又是重中之重——删除节点。
首先绘制完的完整的满二叉树,有 d e p dep dep 行,有 2 m − 2 × 5 + 2 m − 2 − 1 2^{m-2} \times 5+2^{m-2}-1 2m−2×5+2m−2−1 列(可以自己算一下)。然后我们倒序遍历,用 s s s 表示当前搜索到了几个节点,如果 a s = 0 a_s=0 as=0,那么这个节点就是要删除的。
删除时要注意,还要删掉自己与儿子间、与父亲间的连接线。
就像这样:
for(int i=dep;i>=1;i--)
{
for(int j=1;j<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;j++)
{
if(ans[i][j]=='o')//是节点
{
if(!a[++s])//要删除
{
ans[i][j]=' ';
for(int k=1;ans[i-k][j-k]!='o'&&ans[i-k][j-k]!=' '&&i-k>=1&&j-k>=1;k++)
{
ans[i-k][j-k]=' ';
}
for(int k=1;ans[i-k][j+k]!='o'&&ans[i-k][j+k]!=' '&&i-k>=1&&j+k<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;k++)
{
ans[i-k][j+k]=' ';
}
for(int k=1;ans[i+k][j+k]!='o'&&ans[i+k][j+k]!=' '&&i+k<=dep&&j+k<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;k++)
{
ans[i+k][j+k]=' ';
}
for(int k=1;ans[i+k][j-k]!='o'&&ans[i+k][j-k]!=' '&&i+k<=dep&&j-k>=1;k++)
{
ans[i+k][j-k]=' ';
}//四个方向都有可能
}
}
}
}
最后,再输出即可。
完整 AC Code:
#include
using namespace std;
int m,n,dep=1,a[3005];
char ans[3005][3005];
void del(int x)
{
a[x<<1]=0;
a[x<<1|1]=0;
if(x<=(1<<m-1))
{
del(x<<1);
del(x<<1|1);
}
}
void sol(int x,int sp_lr=3,int sp_rl=1)
{
int h=0;
for(int i=1;i<dep;i++)
{
ans[dep][h+i]=' ';
}
h+=dep;
if(x==0)
{
ans[dep][h]='o';
return;
}
for(int i=1;i<=x;i++)
{
ans[dep][h]='o';
for(int j=1;j<=sp_lr;j++)
{
ans[dep][h+j]=' ';
}
ans[dep][h+sp_lr+1]='o';
for(int j=1;j<=sp_rl;j++)
{
ans[dep][h+sp_lr+1+j]=' ';
}
h+=sp_lr+sp_rl+2;
}
dep++;
h=0;
for(int i=sp_lr-2;i>=1;i-=2)
{
for(int j=1;j<=x;j++)
{
for(int k=1;k<dep;k++)
{
ans[dep][h+k]=' ';
}
h+=dep;
ans[dep][h]='/';
for(int k=1;k<=i;k++)
{
ans[dep][h+k]=' ';
}
h+=i+1;
ans[dep][h]='\\';
for(int k=1;k<=dep;k++)
{
ans[dep][h+k]=' ';
}
h+=dep;
}
dep++;
h=0;
}
sol(x>>1,sp_lr==3?5:(sp_lr+1<<1)-1,sp_lr==3?5:(sp_lr+1<<1)-1);
}
int main()
{
cin>>m>>n;
for(int i=1;i<(1<<m);i++)
{
a[i]=1;
}
while(n--)
{
int i,j;
cin>>i>>j;
int k=(1<<i-1)+j-1;
a[k]=0;
a[k<<1]=0;
a[k<<1|1]=0;
del(k<<1);
del(k<<1|1);
}
sol(1<<m-1>>1);
int s=0;
for(int i=dep;i>=1;i--)
{
for(int j=1;j<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;j++)
{
if(ans[i][j]=='o')
{
if(!a[++s])
{
ans[i][j]=' ';
for(int k=1;ans[i-k][j-k]!='o'&&ans[i-k][j-k]!=' '&&i-k>=1&&j-k>=1;k++)
{
ans[i-k][j-k]=' ';
}
for(int k=1;ans[i-k][j+k]!='o'&&ans[i-k][j+k]!=' '&&i-k>=1&&j+k<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;k++)
{
ans[i-k][j+k]=' ';
}
for(int k=1;ans[i+k][j+k]!='o'&&ans[i+k][j+k]!=' '&&i+k<=dep&&j+k<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;k++)
{
ans[i+k][j+k]=' ';
}
for(int k=1;ans[i+k][j-k]!='o'&&ans[i+k][j-k]!=' '&&i+k<=dep&&j-k>=1;k++)
{
ans[i+k][j-k]=' ';
}
}
}
}
}
for(int i=dep;i>=1;i--)
{
for(int j=1;j<=(1<<m-1>>1)*5+(1<<m-1>>1)-1;j++)
{
cout<<ans[i][j];
}
cout<<endl;
}
return 0;
}