前面通过做题加深了对BFS的了解,这里就做一些难度较大的题目,提升一下
题目1:
P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
由数字 0 组成的方阵中,有一任意形状的由数字 1 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 2。例如:6×6 的方阵(n=6),涂色前和涂色后的方阵如下:
如果从某个 0 出发,只向上下左右 4 个方向移动且仅经过其他 0 的情况下,无法到达方阵的边界,就认为这个 0 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内的 0 是连通的(两两之间可以相互到达)。
0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 1 0 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 1 2 1 1 1 1 1 1 1
输入格式
每组测试数据第一行一个整数 n(1≤n≤30)。
接下来 n 行,由 0 和 1 组成的 n×n 的方阵。
方阵内只有一个闭合圈,圈内至少有一个 0。
输出格式
已经填好数字 2 的完整方阵。
输入输出样例1
输入
6 0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 1 1 0 0 0 0 1 1 1 1 1 1 1
输出
0 0 0 0 0 0 0 0 1 1 1 1 0 1 1 2 2 1 1 1 2 2 2 1 1 2 2 2 2 1 1 1 1 1 1 1
输入输出样例2
输入
6
0 0 1 1 1 0
1 1 1 0 1 0
1 0 0 0 0 1
1 1 0 1 1 1
0 1 0 1 0 0
0 1 1 1 0 0
输出
0 0 1 1 1 0
1 1 1 2 1 0
1 2 2 2 2 1
1 1 2 1 1 1
0 1 2 1 0 0
0 1 1 1 0 0
前面我们用DFS算法,完成该题,但是如果n的值太大,可能会时间超限,所以这里用BFS算法来编写程序,节省一些时间。
代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define M 100
#define x first //宏定义first,second
#define y second
int n;
int g[M][M];
bool st[M][M]; //将闭合圈外的 0标记
PII q[M*M]; //数组实现对列
int dx[4]={-1,0,1,0}; //四个方向
int dy[4]={0,1,0,-1};
void bfs(int x1,int y1){
q[0]={x1,y1};
st[x1][y1]=true;
int hh=0,tt=0;
while(hh<=tt){
auto t=q[hh++]; //取出队头,再弹出对头
for(int i=0;i<4;i++){
int a=t.x+dx[i];
int b=t.y+dy[i];
if(a<0||a>n+1||b<0||b>n+1)continue; //在原有边界上 +1,避免样例 2的情况出错,所以在外加上一圈 0
if(g[a][b]==1)continue;
if(st[a][b])continue;
st[a][b]=true; //将闭合圈外的 0标记
q[++tt]={a,b};
}
}
return ;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
scanf("%d",&g[i][j]);
}
}
bfs(0,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(g[i][j]==0&&!st[i][j]){ //当闭合圈外所有的 0被标记,则将矩阵中没有被标记的 0,赋值为 2
g[i][j]=2;
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%d ",g[i][j]); //最后输出处理后的矩阵
}
printf("\n");
}
return 0;
}
题目2:
题目描述
贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。
如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。
根据预报,一共有 M 颗流星 (1≤M≤50,000) 会坠落在农场上,其中第 i 颗流星会在时刻 Ti(0≤Ti≤1000)砸在坐标为(Xi,Yi)(0≤Xi≤300,0≤Yi≤300) 的格子里。流星的力量会将它所在的格子,以及周围 4 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。
贝茜在时刻 0 开始行动,她只能在第一象限中,平行于坐标轴行动,每 1 个时刻中,她能移动到相邻的(一般是 4 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 t 被流星撞击或烧焦,那么贝茜只能在 t 之前的时刻在这个格子里出现。 贝茜一开始在(0,0)。
请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 −1。
输入格式
共 M+1 行,第 1 行输入一个整数 M,接下来的 M 行每行输入三个整数分别为 Xi,Yi,Ti。
输出格式
贝茜到达安全地点所需的最短时间,如果不可能,则为 −1。
输入输出样例
输入
4 0 0 2 2 1 2 1 1 2 0 3 5
输出
5
代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define M 400
#define x first
#define y second
int m;
int dist[M][M];
int fire[M][M]; //记录流星降落在(x1,y1)的时间
PII q[M*M];
int dx[4]={-1,0,1,0};
int dy[4]={0,1,0,-1};
int bfs(){
q[0]={0,0};
dist[0][0]=0;
int hh=0,tt=0;
while(hh<=tt){
auto t=q[hh++];
for(int i=0;i<4;i++){
int a=t.x+dx[i];
int b=t.y+dy[i];
if(a<0||b<0)continue;
if(dist[a][b])continue;
if(dist[t.x][t.y]+1>=fire[a][b])continue; //如果贝茜到达这里的时间大于等于流星砸在这里的时间,则说明不安全
dist[a][b]=dist[t.x][t.y]+1; //搜索的次数,即贝茜移动到此处的时间
q[++tt]={a,b};
if(fire[a][b]==0x3f3f3f3f)return dist[a][b]; //==0x3f3f3f3f改成>=1e9
}
}
return -1;
}
int main(){
scanf("%d",&m);
memset(fire,0x3f,sizeof(fire));
while(m--){
int x1,y1,t;
scanf("%d%d%d",&x1,&y1,&t);
fire[x1][y1]=min(t,fire[x1][y1]);
for(int i=0;i<4;i++){
int a=x1+dx[i];
int b=y1+dy[i];
if(a<0||a>301||b<0||b>301)continue;
fire[a][b]=min(t,fire[a][b]); //取最早降落在(x1,y1)的那个流星的时间
}
}
int res=bfs();
printf("%d\n",res);
return 0;
}
题目3:
P2658 汽车拉力比赛 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
博艾市将要举行一场汽车拉力比赛。
赛场凹凸不平,所以被描述为M*N的网格来表示海拔高度(1≤ M,N ≤500),每个单元格的海拔范围在0到10^9之间。
其中一些单元格被定义为路标。组织者希望给整个路线指定一个难度系数D,这样参赛选手从任一路标到达别的路标所经过的路径上相邻单元格的海拔高度差不会大于D。也就是说这个难度系数D指的是保证所有路标相互可达的最小值。任一单元格和其东西南北四个方向上的单元格都是相邻的。
输入格式
第一行两个整数M和N。第2行到第M+1行,每行N个整数描述海拔高度。第2+M行到第1+2M
行,每行N个整数,每个数非0即1,1表示该单元格是一个路标。
输出格式
一个整数,即赛道的难度系数D。
输入输出样例
输入
3 5 20 21 18 99 5 19 22 20 16 26 18 17 40 60 80 1 0 0 0 1 0 0 0 0 0 0 0 0 0 1
输出
21
代码:
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
#define M 510
#define x first
#define y second
PII q[M*M];
int m,n;
int high[M][M]; //海拔高度
int flag[M][M]; //路标
int flags=0; //统计路标总数
int fx=0,fy=0;
bool st[M][M]; //记录路标是否走过
int dx[4]={-1,0,1,0}; //四个方向
int dy[4]={0,1,0,-1};
bool bfs(int mid){
q[0]={fx,fy};
st[fx][fy]=true;
int res=1; //记录已经过的路标数量
int hh=0,tt=0;
while(hh<=tt){
auto t=q[hh++]; //取出对头,再弹出对头
for(int i=0;i<4;i++){
int a=t.x+dx[i];
int b=t.y+dy[i];
if(a<1||a>m||b<1||b>n)continue;
if(st[a][b])continue;
if(abs(high[t.x][t.y]-high[a][b])>mid)continue;
q[++tt]={a,b}; //符合条件,则入队
st[a][b]=true; //记录该点已过
if(flag[a][b]==1){
res++; //经过一次路标,cnt++
if(res==flags){ //当经过的路标的数量等于路标总数
return true;
}
}
}
}
return false;
}
int main(){
scanf("%d%d",&m,&n);
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&high[i][j]);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
scanf("%d",&flag[i][j]);
if(flag[i][j]==1){
flags++;
}
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=n;j++){
if(flag[i][j]==1){
fx=i,fy=j;
break;
}
}
}
int l=-1,r=1e9;
while(l+1<r){
int mid=(l+r)/2;
memset(q,0,sizeof(q));
memset(st,false,sizeof(st));
if(bfs(mid)){
r=mid;
}else{
l=mid;
}
}
printf("%d\n",r);
return 0;
}