csp2015-12-3_画图(dfs/bfs搜索)
题目描述
用 ASCII 字符来画图是一件有趣的事情,并形成了一门被称为 ASCII Art 的艺术。例如,下图是用 ASCII 字符画出来的 CSPRO 字样。
本题要求编程实现一个用 ASCII 字符来画图的程序,支持以下两种操作:
画线:给出两个端点的坐标,画一条连接这两个端点的线段。简便起见题目保证要画的每条线段都是水平或者竖直的。水平线段用字符 - 来画,竖直线段用字符 | 来画。如果一条水平线段和一条竖直线段在某个位置相交,则相交位置用字符 + 代替。
填充:给出填充的起始位置坐标和需要填充的字符,从起始位置开始,用该字符填充相邻位置,直到遇到画布边缘或已经画好的线段。注意这里的相邻位置只需要考虑上下左右 4 个方向,如下图所示,字符 @ 只和 4 个字符 * 相邻。
Input:
第1行有三个整数m, n和q。m和n分别表示画布的宽度和高度,以字符为单位。q表示画图操作的个数。
第2行至第q + 1行,每行是以下两种形式之一:
0 x1 y1 x2 y2:表示画线段的操作,(x1, y1)和(x2, y2)分别是线段的两端,满足要么x1 = x2 且y1 ≠ y2,要么 y1 = y2 且 x1 ≠ x2。
1 x y c:表示填充操作,(x, y)是起始位置,保证不会落在任何已有的线段上;c 为填充字符,是大小写字母。
画布的左下角是坐标为 (0, 0) 的位置,向右为x坐标增大的方向,向上为y坐标增大的方向。这q个操作按照数据给出的顺序依次执行。画布最初时所有位置都是字符 .(小数点)。
Output:
输出有n行,每行m个字符,表示依次执行这q个操作后得到的画图结果。
题目思路
这个题目不算很难。我们可以分析它所给的两种操作,分别是画线,填充。画线很简单,就是单纯的遍历,几个for循环加上一些if判断就可以解决。重点是在填充部分,类似于涂色问题,可以明显的知道要使用bfs或者dfs来实现,这里我使用的是dfs。在填充的时候如果遇到线段或者已经填充过的点就不需要填充直接回溯,否则填充并且更新vis数组,这里的vis数组是用来记录一次填充过程中哪些点已经被填充过。如果超过边界也是直接回溯。然后再填充相邻的上下左右四个方向,这里表示四个方向我使用了dx和dy数组来表示x和y方向上的坐标的变化值。另外这里我是使用二维的字符数组来存储数据的,最后输出的时候需要注意一下输出的顺序。
代码实现
#include <bits/stdc++.h>
using namespace std;
#define _for(i,a,b) for(int i = (a); i < b; i++)
#define _rep(i,a,b) for(int i = (a); i <= b; i++)
int m , n ,q;
int X1 , Y1 , X2 , Y2;
//int x , y , c;
char graph[110][110];
bool vis[110][110]; //记忆化,记录是否填充
//上右下左
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
void drawline() //画线
{
if(X1 == X2) //x方向相同,在y方向上画竖线
{
if(Y1 > Y2) //从Y2 -> Y1
{
_rep(i,Y2,Y1)
{
if(graph[X1][i] == '-' || graph[X1][i] == '+') //这里需要注意遇到+时不覆盖掉,遇到|时也不覆盖掉
graph[X1][i] = '+';
else graph[X1][i] = '|';
}
}else
{ //从Y1 -> Y2
_rep(i,Y1,Y2)
{
if(graph[X1][i] == '-' || graph[X1][i] == '+')
graph[X1][i] = '+';
else graph[X1][i] = '|';
}
}
}
else //y方向上相同,在x方向画横线
{
if(X1 > X2) //从X2 -> X1
{
_rep(i,X2,X1)
{
if(graph[i][Y1] == '|'|| graph[i][Y1] == '+') //注意
graph[i][Y1] = '+';
else graph[i][Y1] = '-';
}
}
else
{ //从X1 -> X2
_rep(i,X1,X2)
{
if(graph[i][Y1] == '|'|| graph[i][Y1] == '+')
graph[i][Y1] = '+';
else graph[i][Y1] = '-';
}
}
}
}
void fill(int x,int y,char c) //填充
{
if(x >= 0 && x < m && y >= 0 && y < n ) //边界
{
//填充是遇到线段或者已经填充过就不需要填充
if(graph[x][y] != '-' && graph[x][y] != '|' && graph[x][y] != '+' && !vis[x][y])
{//注意这里的vis数组的使用是避免死循环
graph[x][y] = c;
vis[x][y] = true;
}
else return;
}
else return;
_for(i,0,4) //继续填充相邻位置
fill(x + dx[i] , y + dy[i] , c);
}
void print() //输出
{
for(int i = n-1; i >= 0; i--)
{
_for(j,0,m)
{
printf("%c",graph[j][i]);
}
printf("\n");
}
}
int main()
{
int k;
scanf("%d%d%d",&m,&n,&q);
memset(graph , '.' , sizeof(graph)); //初始化
_for(i,0,q)
{
scanf("%d",&k); //判断是画线还是填充
if(k)
{//填充
int x , y ;
char c;
scanf("%d%d %c",&x,&y,&c); //读入的时候需要注意空格也是字符,需要处理
memset(vis , false , sizeof(vis));//初始化
fill(x,y,c);
}
else
{//画线
scanf("%d%d%d%d",&X1,&Y1,&X2,&Y2);
drawline();
}
}
print();
}
总结
在做这个题目的过程中我遇到了一些问题:
1.首先是变量y0,y1等在定义的时候,由于在*“math.h”头文件里面将其作为函数名,所以我们在定义的时候就会出现错误。
2.然后是vis数组的初始化,由于vis数组时用来记忆化处理,记录每次填充的时候是否已经被填充过,所以每次填充之前都需要初始化一次。
3.然后是使用scanf读入数字和字符的时候,需要注意空格和回车也是字符,需要使用getchar处理或者如果只是空格可以在scanf*中添加一个空格。
scanf("%d%d %c",&x,&y,&c); //读入的时候需要注意空格也是字符,需要处理
4.另外在这个题目中需要注意到,在画线的过程中如果遇到*+,不能用横线或者竖线覆盖,必须保留为+*
总之,这个题目并不是很难,但是却暴露出了不少问题,以后做题需要多多注意。