题目1:
P1506 拯救oibh总部 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目很好,我分析的尽量充分一点
#include<iostream>
#include<cstdio>
using namespace std;
char ch;
int x,y,ans,map[550][550],dx[5]={0,1,-1,0,0},dy[5]={0,0,0,1,-1};//dx,dy是上下左右四个方向(把0空了过去)
void dfs(int m,int n)
{
if(m<0||n<0||m>x+1||n>y+1||map[m][n]) //如果越界或有障碍就回溯 ---------1
return;
map[m][n]=2;
for(int i=1;i<=4;i++) //上下左右搜索
dfs(m+dx[i],n+dy[i]);------------------------------------2
}
int main()
{
scanf("%d%d",&x,&y);
for(int i=1;i<=x;i++) //习惯处理成数字地图
for(int j=1;j<=y;j++)
{
cin>>ch;
if(ch=='0')
map[i][j]=0;
else map[i][j]=1;--------------------------------3
}
dfs(0,0); //洪水开始泛滥 --------------------------------4
for(int i=1;i<=x;i++) //寻找没有被洪水袭击的点即未被染色的点
for(int j=1;j<=y;j++)
if(!map[i][j])-------------------------------------5
ans++;
printf("%d",ans);
return 0;
}
理解点1:为什么说是大于x+1和大于y+1这是为了防止有遗漏
2:是遍历四个方向的常规写法
3:这个地方是首先遍历地图将想要的0赋值为0,将*赋值为1,这样便于理解
4:当这个值还是0不是2的时候说明它被*号所包围了,因此就是你要的值
理解,为啥被包围的0遍历不到呢??
答:首先看理解1出、处的条件,当值为1即碰见*的时候是不向四个方向遍历的(return语句的作用),因此当四个方向都是*号的时候那么中间的就永远遍历不到了呀。
题目2:
P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
仿照第一题的写法,真的是果然开朗呀。洛谷一遍过了。
#include<iostream>
#include<cstdio>
using namespace std;
int n;
int a[50][50];
int map[50][50];
int dirx[5] = {0,-1,1,0,0 };
int diry[5] = {0,0,0,-1,1 };
void dfs(int x, int y)
{
if (x<0||y<0||x > n + 1 || y > n + 1 || map[x][y])
return;
map[x][y] = 3;//注意这条语句千万不能放到最前面,要先判断出界与否,在执行下面的语句
for (int i = 1; i <=4; i++)
dfs(x + dirx[i], y + diry[i]);
}
int main()
{
cin >> n;
for(int i=1;i<=n;i++)
for (int j =1; j <=n; j++)
{
cin >> a[i][j];
if (a[i][j] == 0)
map[i][j] = 0;
else
map[i][j] = 1;
}
dfs(0, 0);
for (int i = 1; i <=n; i++)
for (int j = 1; j <=n; j++)
{
if (map[i][j] == 0)//主要是这里不太一样,当你被包围住那就还是0,那就按照要求赋值2
a[i][j] = 2;
if (map[i][j] == 3)
a[i][j] = 0;//把你赋值过的再赋值回去,这里是点睛之笔
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
cout << a[i][j] << " ";
printf("\n");
}
return 0;
}
这两篇题目应该是涂色问题的典型了。好好理解。
题目三:
CF445A DZY Loves Chessboard - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
这道题我的收获的最大值之处在于对不重复标记的解决办法。
#include<iostream>
using namespace std;
int m, n;
int dirx[4] = { 1,-1,0,0 };
int diry[4] = { 0,0,1,-1 };
char map[100][100];
void dfs(char f, int x, int y)
{
if (x<0 || y<0 || x>n + 1 || y>m + 1 || map[x][y])
return;
map[x][y] = f;------------------------------------------------1
for (int i = 0; i < 4; i++)
dfs(f=='W'?'B':'W', x + dirx[i], y + diry[i]);------------2
}
int main()
{
cin >> n >> m;
for(int i=1;i<=n;i++)
for (int j = 1; j <= m; j++)
{
cin >> map[i][j];
if (map[i][j] =='.')
map[i][j] = 0;
else
map[i][j] = 1;
}
dfs('B',0, 0);------------------------------------------------3
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cout << map[i][j];
}
printf("\n");
}
return 0;
}
可以发现,其他代码和模板几乎没有任何的区别,区别就在于三个理解点
理解点1:这个地方是直接赋值字符
2.最最巧妙之处了,当f==w时,f就是w,当f==B时,f反而等于w,这就是a>b?a:b;语句的用法
巧妙之处避免相邻的字符是一样的。
3.dfs函数多加一个变量而已。
此外,题解里面的也很好,符合dfs的函数模板
#include<cstdio>
#include<iostream>
using namespace std;
int n, m;
char map[105][105]; //棋盘
int dx[4] = { 0, 1, 0, -1 };//斜度偏移数组,上下左右
int dy[4] = { 1, 0, -1, 0 };
void dfs(char f, int x, int y)//F与上一次搜索的点的棋子不同, X是它的纵坐标,y是横坐标
{
map[x][y] = f;//保存
for (int i = 0; i < 4; i++)//扫描上下左右
{
int x_new = x + dx[i], y_new = y + dy[i];//新位置
if (map[x_new][y_new] == '.' && x_new >= 1 && x_new <= n && y_new >= 1 && y_new <= m)//判断是否非法
{
dfs(f == 'W' ? 'B' : 'W', x_new, y_new);//搜索下一个
}
}
}
int main()
{
cin >> n >> m;//输入
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
cin >> map[i][j];//输入
}
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++)
{
if (map[i][j] == '.') dfs('B', i, j);//如果是好格,直接深搜
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cout << map[i][j];//输出
}
puts("");//换行
}
return 0;
}