搜索篇 ——洛谷
一、马的遍历
题目链接:P1443 马的遍历 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
有一个 n * m 的棋盘,在某个点 (x, y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。(中国象棋里面的马,不知道怎么走的小伙伴可以百度)。
输入格式
输入只有一行四个整数,分别为 n, m, x, y。
输出格式
一个 n * m 的矩阵,代表马到达某个点最少要走几步(左对齐,宽 5 格,不能到达则输出 −1)。
输入输出样例
输入 #1
3 3 1 1
输出 #1
0 3 2
3 -1 1
2 1 4
数据规模
对于全部的测试点,保证 1 ≤ x ≤ n ≤ 400, 1 ≤ y ≤ m ≤ 400。
广搜(BFS)模板题,直接按模板套路写即可
容易犯的错误,得耽误不少时间debug
初始化,出队,标记为走过,以及更新步数
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 500;
const int dx[8] = {-1,1,-1,1,-2,2,-2,2};
const int dy[8] = {2,2,-2,-2,1,1,-1,-1};
int vis[N][N];
int step[N][N];
int main(){
int n,m,sx,sy; cin >> n >> m >> sx >> sy;
queue<PII> que;
memset(step,-1,sizeof step); memset(vis,0,sizeof vis);
vis[sx][sy] = 1, step[sx][sy] = 0;
que.push({sx,sy});
while(!que.empty()){
PII temp = que.front(); que.pop();
for(int i = 0; i < 8; i++){
int nx = dx[i] + temp.first, ny = dy[i] + temp.second;
if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny]) continue;
step[nx][ny] = step[temp.first][temp.second] + 1;
que.push({nx,ny});
vis[nx][ny] = 1;
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++)
printf("%-5d",step[i][j]);
printf("\n");
}
return 0;
}
二、好奇怪的游戏
题目链接:P1747 好奇怪的游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
爱与愁大神坐在公交车上无聊,于是玩起了手机。一款奇怪的游戏进入了爱与愁大神的眼帘:***(游戏名被打上了马赛克)。这个游戏类似象棋,但是只有黑白马各一匹,在点x1,y1和x2,y2上。它们得从点x1,y1和x2,y2走到1,1。这个游戏与普通象棋不同的地方是:马可以走“日”,也可以像象走“田”。现在爱与愁大神想知道两匹马到1,1的最少步数,你能帮他解决这个问题么?
输入格式
第1行:两个整数x1,y1
第2行:两个整数x2,y2
输出格式
第1行:黑马到1,1的步数
第2行:白马到1,1的步数
输入输出样例
输入 #1
12 16
18 10
输出 #1
8
9
数据范围
100%数据:x1,y1,x2,y2<=20
还是bfs,只不过对于第一题变成了12个方向,然后分两次搜索两个点的答案就行了
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 60;
const int dx[] = {-1,1,-1,1,-2,2,-2,2,2,2,-2,-2};
const int dy[] = {2,2,-2,-2,1,1,-1,-1,-2,2,-2,2};
int vis[N][N];
struct Node {
int x, y, step;
Node(int x, int y, int step){
this->x = x;
this->y = y;
this->step = step;
}
};
int bfs(int sx, int sy){
queue<Node> que;
que.push(Node(sx, sy, 0));
vis[sx][sy] = 1;
while(!que.empty()){
int x = que.front().x, y = que.front().y, step = que.front().step;
que.pop();
for(int i = 0; i < 12; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > 50 || ny < 1 || ny > 50 || vis[nx][ny]) continue;
if(nx == 1 && ny == 1) return step + 1;
que.push(Node(nx, ny, step + 1));
vis[nx][ny] = 1;
}
}
}
int main(){
int sx,sy;
cin >> sx >> sy;
cout << bfs(sx,sy) << endl;
memset(vis,0,sizeof(vis));
cin >> sx >> sy;
cout << bfs(sx,sy) << endl;
return 0;
}
三、离开中山路
题目链接:P1746 离开中山路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
爱与愁大神买完东西后,打算坐车离开中山路。现在爱与愁大神在x1,y1处,车站在x2,y2处。现在给出一个n×n(n<=1000)的地图,0表示马路,1表示店铺(不能从店铺穿过),爱与愁大神只能垂直或水平着在马路上行进。爱与愁大神为了节省时间,他要求最短到达目的地距离(a[i][j]距离为1)。你能帮他解决吗?
输入格式
第1行:一个数 n
第2行~第n+1行:整个地图描述(0表示马路,1表示店铺,注意两个数之间没有空格)
第n+2行:四个数 x1,y1,x2,y2
输出格式
只有1行:最短到达目的地距离
输入输出样例
输入 #1复制
3
001
101
100
1 1 3 3
输出 #1
4
数据范围
20%数据: n <= 100
100%数据:n <= 1000
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
int step[N][N];
char g[N][N];
int n, sx, sy, ex, ey;;
int bfs(){
queue<PII> que;
que.push({sx,sy});
vis[sx][sy] = 1;
while(!que.empty()){
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > n || vis[nx][ny] || g[nx][ny] == '1') continue;
vis[nx][ny] = 1;
que.push({nx,ny});
step[nx][ny] = step[x][y] + 1;
if(nx == ex && ny == ey) return step[nx][ny];
}
}
return -1;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++)
cin >> g[i][j];
cin >> sx >> sy >> ex >> ey;
cout << bfs() << endl;
return 0;
}
四、Mzc和男家丁的游戏
题目链接:P2298 Mzc和男家丁的游戏 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
mzc家很有钱(开玩笑),他家有n个男家丁(做过上一弹的都知道)。他把她们召集在了一起,他们决定玩捉迷藏。现在mzc要来寻找他的男家丁,大家一起来帮忙啊!
由于男家丁数目不多,再加上mzc大大的找人【laopo】水平很好,所以一次只需要找一个男家丁。
输入格式
第一行有两个数n,m,表示有n行m列供男家丁躲藏,
之后n行m列的矩阵,‘m‘表示mzc,‘d’表示男家丁,‘#’表示不能走,‘.‘表示空地。
输出格式
一行,若有解:输出一个数step,表示找到男家丁的最短移动次数。
若无解:输出“No Way!”。
输入输出样例
输入 #1
5 6
.#..#.
....#.
d.....
#####.
m.....
输出 #1
12
数据范围
3 < = m, n <= 2000
由于mzc大大十分着急,所以他只能等待1S。
记录起点和终点,改一下模板就能过了
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int N = 2e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
char g[N][N];
int n, m, sx, sy, ex, ey;;
struct Node {
int x, y, step;
Node(int x, int y, int step){
this->x = x;
this->y = y;
this->step = step;
}
};
int bfs(){
queue<Node> que;
que.push(Node(sx, sy, 0));
vis[sx][sy] = 1;
while(!que.empty()){
int x = que.front().x, y = que.front().y, step = que.front().step;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny] || g[nx][ny] == '#') continue;
vis[nx][ny] = 1;
que.push(Node(nx, ny, step + 1));
if(nx == ex && ny == ey) return step + 1;
}
}
return -1;
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++){
cin >> g[i][j];
if(g[i][j] == 'm') sx = i, sy = j;
if(g[i][j] == 'd') ex = i, ey = j;
}
int ans = bfs();
if(ans == -1) cout<<"No Way!"<<endl;
else cout << ans << endl;
return 0;
}
五、血色先锋队
题目链接:P1332 血色先锋队 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
军团是一个 nn 行 mm 列的矩阵,每个单元是一个血色先锋军的成员。感染瘟疫的人,每过一个小时,就会向四周扩散瘟疫,直到所有人全部感染上瘟疫。你已经掌握了感染源的位置,任务是算出血色先锋军的领主们感染瘟疫的时间,并且将它报告给巫妖王,以便对血色先锋军进行一轮有针对性的围剿。
输入格式
第 11 行:四个整数 nn,mm,aa,bb,表示军团矩阵有 nn 行 mm 列。有 aa 个感染源,bb 为血色敢死队中领主的数量。
接下来 aa 行:每行有两个整数 xx,yy,表示感染源在第 xx 行第 yy 列。
接下来 bb 行:每行有两个整数 xx,yy,表示领主的位置在第 xx 行第 yy 列。
输出格式
第 11 至 bb 行:每行一个整数,表示这个领主感染瘟疫的时间,输出顺序与输入顺序一致。如果某个人的位置在感染源,那么他感染瘟疫的时间为 00。
输入输出样例
输入 #1
5 4 2 3
1 1
5 4
3 3
5 3
2 4
输出 #1
3
1
3
说明/提示
输入输出样例 1 解释
如下图,标记出了所有人感染瘟疫的时间以及感染源和领主的位置。
数据规模与约定
对于 100% 的数据,保证 1 ≤ n , m ≤ 500,1 ≤ a , b ≤ 105。
code1:爆搜取巧卡样例到19000开O2优化过去了
code2:基础搜索类型之一,将病毒原点全部加入队列,然后对其展开搜索即可。
code1
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
int g[N][N];
int n, m, a, b;
int flag = 1;
void bfs(int sx, int sy){
queue<PII> que;
que.push({sx,sy});
vis[sx][sy] = 1;
int count = 0;
while(!que.empty() && count < 19000){
count++;
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > n || vis[nx][ny] == flag) continue;
vis[nx][ny] = flag;
que.push({nx,ny});
g[nx][ny] = min(g[nx][ny], g[x][y] + 1);
}
}
}
int main(){
cin >> n >> m >> a >> b;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
g[i][j] = inf;
}
}
int tx, ty;
for(int i = 0; i < a; i++){
cin >> tx >> ty;
g[tx][ty] = 0;
bfs(tx, ty);
flag++;//标记此次是否被搜索过
}
for(int i = 0; i < b; i++){
cin >> tx >> ty;
cout << g[tx][ty] << endl;
}
return 0;
}
code2:
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
struct Node {
int x, y, step;
Node(int x, int y, int step){
this->x = x;
this->y = y;
this->step = step;
}
};
int vis[N][N];
int g[N][N];
int n, m, a, b;
int flag = 1;
queue<Node> que;
void init(){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
g[i][j] = inf;
}
}
}
void bfs(){
while(!que.empty()){
int x = que.front().x, y = que.front().y, step = que.front().step;
que.pop();
g[x][y] = min(g[x][y], step);
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny]) continue;
vis[nx][ny] = 1;
que.push(Node(nx, ny, step + 1));
}
}
}
int main(){
cin >> n >> m >> a >> b;
init();
int tx, ty;
for(int i = 0; i < a; i++){
cin >> tx >> ty;
que.push(Node(tx,ty,0));
g[tx][ty] = 0;
vis[tx][ty] = 1;
}
bfs();
for(int i = 0; i < b; i++){
cin >> tx >> ty;
cout << g[tx][ty] << endl;
}
return 0;
}
六、求细胞数量
题目链接:P1451 求细胞数量 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
一矩形阵列由数字 0 到 9 组成,数字 1到 9代表细胞,细胞的定义为沿细胞数字上下左右若还是细胞数字则为同一细胞,求给定矩形阵列的细胞个数。
输入格式
第一行两个整数代表矩阵大小 n 和 m。
接下来 n 行,每行一个长度为 m 的只含字符 0
到 9
的字符串,代表这个 n×m 的矩阵。
输出格式
一行一个整数代表细胞个数。
输入输出样例
输入 #1
4 10
0234500067
1034560500
2045600671
0000000089
输出 #1
4
说明/提示
数据规模与约定
对于 100% 的数据,保证 1 ≤ n , m ≤ 100。
其实就是求有多少个块,那么我们对每个没有访问过的细胞,搜索他的同胞即可
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e2+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
char g[N][N];
int n, m, ans;
queue<PII> que;
void bfs(int sx, int sy){
ans ++;
que.push({sx,sy});
vis[sx][sy] = 1;
while(!que.empty()){
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny] || g[nx][ny] == '0') continue;
vis[nx][ny] = 1;
que.push({nx, ny});
}
}
}
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> g[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(g[i][j] != '0' && !vis[i][j]) bfs(i, j);//记得判断该细胞是否被访问过,否则会重复很多
}
}
cout << ans << endl;
return 0;
}
七、01迷宫
题目链接:P1141 01迷宫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
有一个仅由数字0与1组成的n*n格迷宫。若你位于一格0上,那么你可以移动到相邻4格中的某一格1上,同样若你位于一格1上,那么你可以移动到相邻4格中的某一格0上。
你的任务是:对于给定的迷宫,询问从某一格开始能移动到多少个格子(包含自身)。
输入格式
第1行为两个正整数n,m。
下面n行,每行n个字符,字符只可能是0或者1,字符之间没有空格。
接下来m行,每行2个用空格分隔的正整数 i , j,对应了迷宫中 第 i 行 第 j 列 的一个格子,询问从这一格开始能移动到多少格。
输出格式
m 行,对于每个询问输出相应答案。
输入输出样例
输入 #1
2 2
01
10
1 1
2 2
输出 #1
4
4
说明/提示
所有格子互相可达。
对于20%的数据,n ≤ 10;
对于40%的数据,n ≤ 50;
对于50%的数据,m ≤ 5;
对于60%的数据,n ≤ 100,m ≤ 100;
对于100%的数据,n ≤ 1000, m ≤ 100000。
记忆化深搜或者宽搜即可
因为只要是联通的块,他们的个数肯定都是一样的,所以在每次搜索以后记录值即可
map记录该次搜索过的坐标,但是,每次使用map还得clear,复杂度又蹭蹭上来了,得开O2才能过
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<unordered_map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
char g[N][N];
int value[N*N][2];
int n, m, flag = 1;
int ans = 0;
map<pair<int,int>,int> mp;
void dfs(int sx, int sy){
if(sx < 1 || sx > n || sy < 1 || sy > n || vis[sx][sy]) return ;
ans++;
vis[sx][sy] = 1;
mp[{sx,sy}] = 1;
for(int i = 0; i < 4; i++){
int nx = dx[i] + sx, ny = dy[i] + sy;
if(g[nx][ny] != g[sx][sy]) dfs(nx,ny);
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g[i][j];
}
}
int tx, ty;
for(int i = 0; i < m; i++){
ans = 0;
cin >> tx >> ty;
if(vis[tx][ty] > 0){
cout << vis[tx][ty] << endl;
}else{
mp.clear();
dfs(tx, ty);
for(auto &c : mp)
vis[c.first.first][c.first.second] = ans;
cout << ans << endl;
}
}
return 0;
}
贴一个用数组模拟map的,复杂度就在允许的范围内啦
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<unordered_map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
char g[N][N];
int value[N*N][2];
int n, m, flag = 1;
int ans = 0;
void dfs(int sx, int sy){
if(sx < 1 || sx > n || sy < 1 || sy > n) return ;
if(vis[sx][sy]) return ;
ans++;
vis[sx][sy] = 1;
value[ans][0] = sx;
value[ans][1] = sy;
for(int i = 0; i < 4; i++){
int nx = dx[i] + sx, ny = dy[i] + sy;
if(g[nx][ny] != g[sx][sy]) dfs(nx,ny);
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g[i][j];
}
}
int tx, ty;
for(int i = 0; i < m; i++){
ans = 0;
cin >> tx >> ty;
if(vis[tx][ty] > 0){
cout << vis[tx][ty] << endl;
}else{
dfs(tx, ty);
for(int j = 1; j <= ans; j++)
vis[value[j][0]][value[j][1]] = ans;
cout << ans << endl;
}
}
return 0;
}
八、填涂颜色
题目链接:P1162 填涂颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
由数字0组成的方阵中,有一任意形状闭合圈,闭合圈由数字1构成,围圈时只走上下左右4个方向。现要求把闭合圈内的所有空间都填写成22.例如:6×6的方阵(n=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
输入格式
每组测试数据第一行一个整数 n (1 ≤ n ≤ 30)
接下来n行,由0和1组成的n×n的方阵。
方阵内只有一个闭合圈,圈内至少有一个0。
//感谢黄小U饮品指出本题数据和数据格式不一样. 已修改(输入格式)
输出格式
已经填好数字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
输出 #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
说明/提示
1 ≤ n ≤ 30
由于只有一圈,厚度为1,所以我们找到第一个1那么他的右下角的0就可以当作我们搜索的起点,然后正常搜索标记即可
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int g[N][N];
int n;
void bfs(int sx, int sy){
queue<PII> que;
que.push({sx,sy});
g[sx][sy] = 2;
while(!que.empty()){
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > n || g[nx][ny] == 2 || g[nx][ny] == 1) continue;
g[nx][ny] = 2;
que.push({nx,ny});
}
}
}
int main(){
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(g[i][j] == 1){
bfs(i+1,j+1);
goto f;
}
}
}
f:;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cout << g[i][j] << " ";
}
cout << endl;
}
return 0;
}
九、入门
题目链接:P1683 入门 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
不是任何人都可以进入桃花岛的,黄药师最讨厌象郭靖一样呆头呆脑的人。所以,他在桃花岛的唯一入口处修了一条小路,这条小路全部用正方形瓷砖铺设而成。有的瓷砖可以踩,我们认为是安全的,而有的瓷砖一踩上去就会有喷出要命的毒气,那你就死翘翘了,我们认为是不安全的。你只能从一块安全的瓷砖上走到与他相邻的四块瓷砖中的任何一个上,但它也必须是安全的才行。
由于你是黄蓉的朋友,她事先告诉你哪些砖是安全的、哪些砖是不安全的,并且她会指引你飞到第一块砖上(第一块砖可能在任意安全位置),现在她告诉你进入桃花岛的秘密就是:如果你能走过最多的瓷砖并且没有死,那么桃花岛的大门就会自动打开了,你就可以从当前位置直接飞进大门了。
注意:瓷砖可以重复走过,但不能重复计数。
输入格式
第一行两个正整数 W 和 H,分别表示小路的宽度和长度。
以下 HH 行为一个 H×W 的字符矩阵。每一个字符代表一块瓷砖。其中,.
代表安全的砖,#
代表不安全的砖,@
代表第一块砖。
输出格式
输出一行,只包括一个数,即你从第一块砖开始所能安全走过的最多的砖块个数(包括第一块砖)。
输入输出样例
输入 #1
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
输出 #1
59
说明/提示
数据规模与约定
对于全部的测试点,保证1 ≤ W, H≤20。
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
char g[N][N];
int vis[N][N];
int n, m, sx, sy, ans;
void bfs(){
queue<PII> que;
que.push({sx,sy});
vis[sx][sy] = 1;
while(!que.empty()){
ans++;
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > m || vis[nx][ny] || g[nx][ny] == '#') continue;
g[nx][ny] = 1;
vis[nx][ny] = 1;
que.push({nx,ny});
}
}
}
int main(){
ios::sync_with_stdio(false);
cin >> m >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> g[i][j];
if(g[i][j] == '@'){
sx = i, sy = j;
}
}
}
bfs();
cout << ans << endl;
system("pause");
return 0;
}
十、[USACO19JAN]Icy Perimeter S
题目链接:[P5198 USACO19JAN]Icy Perimeter S - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目背景
USACO一月月赛银组第二题
题目描述
Farmer John要开始他的冰激凌生意了!他制造了一台可以生产冰激凌球的机器,然而不幸的是形状不太规则,所以他现在希望优化一下这台机器,使其产出的冰激凌球的形状更加合理。 机器生产出的冰激凌的形状可以用一个N×N(1≤N≤1000)的矩形图案表示,例如:
##....
....#.
.#..#.
.#####
...###
....##
每个’.‘字符表示空的区域,每个’#'字符表示一块1×1的正方形格子大小的冰激凌。
不幸的是,机器当前工作得并不是很正常,可能会生产出多个互不相连的冰激凌球(上图中有两个)。一个冰激凌球是连通的,如果其中每个冰激凌的正方形格子都可以从这个冰激凌球中其他所有的冰激凌格子出发重复地前往东、南、西、北四个方向上相邻的冰激凌格子所到达。
Farmer John想要求出他的面积最大的冰激凌球的面积和周长。冰激凌球的面积就是这个冰激凌球中’#'的数量。如果有多个冰激凌球并列面积最大,他想要知道其中周长最小的冰激凌球的周长。在上图中,小的冰激凌球的面积为2,周长为6,大的冰激凌球的面积为13,周长为22。
注意一个冰激凌球可能在中间有“洞”(由冰激凌包围着的空的区域)。如果这样,洞的边界同样计入冰激凌球的周长。冰激凌球也可能出现在被其他冰激凌球包围的区域内,在这种情况下它们计为不同的冰激凌球。例如,以下这种情况包括一个面积为1的冰激凌球,被包围在一个面积为16的冰激凌球内:
#####
#...#
#.#.#
#...#
#####
同时求得冰激凌球的面积和周长十分重要,因为Farmer John最终想要最小化周长与面积的比值,他称这是他的冰激凌的“冰周率”。当这个比率较小的时候,冰激凌化得比较慢,因为此时冰激凌单位质量的表面积较小。
输入格式
输入的第一行包含N,以下N行描述了机器的生产结果。其中至少出现一个’#'字符。
输出格式
输出一行,包含两个空格分隔的整数,第一个数为最大的冰激凌球的面积,第二个数为它的周长。如果多个冰激凌球并列面积最大,输出其中周长最小的那一个的信息。
输入输出样例
输入 #1
6
##....
....#.
.#..#.
.#####
...###
....##
输出 #1
13 22
面积的计算比较容易,计算联通块的个数,那么周长怎么计算呢?
每次统计周长应该是到边界,或 者是那一条边的方向是 ‘ . ',就是外围啦,那么对周长++即可。
最后按照题意排序输出最前(或者最后)的值即可
if(nx < 1 || nx > n || ny < 1 || ny > n //到边界
|| vis[nx][ny] || g[nx][ny] == '.'){
if(!vis[nx][ny]) c++;//还得去掉访问过的情况,免得周长多加
continue;
}
#include<iostream>
#include<stack>
#include<vector>
#include<string>
#include<cstring>
#include<map>
#include<algorithm>
#include<cmath>
#include<queue>
#define debug cout << "___debug___\n";
using namespace std;
typedef pair<int,int> PII;
const int inf = 1e9;
const int N = 1e3+10;
const int dx[] = {0,1,0,-1};
const int dy[] = {1,0,-1,0};
int vis[N][N];
char g[N][N];
int n, square, cir;
vector<PII> vec;
void bfs(int sx, int sy){
int s = 0, c = 0;//面积 s 周长 c
queue<PII> que;
que.push({sx,sy});
vis[sx][sy] = 1;
while(!que.empty()){
s++;
int x = que.front().first, y = que.front().second;
que.pop();
for(int i = 0; i < 4; i++){
int nx = x + dx[i], ny = y + dy[i];
if(nx < 1 || nx > n || ny < 1 || ny > n || vis[nx][ny] || g[nx][ny] == '.'){
if(!vis[nx][ny]) c++;//还得去掉访问过的情况,免得周长多加
continue;
}
vis[nx][ny] = 1;
que.push({nx, ny});
}
}
vec.push_back({s,c});//first存面积, second 存周长
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> g[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(g[i][j] != '.' && !vis[i][j]) bfs(i, j);
}
}
sort(vec.begin(),vec.end(),[&](PII a, PII b){
if(a.first != b.first) return a.first > b.first;//降序排序,面积大的在前面
return a.second < b.second;//同面积的周长小的在前面
});
square = vec[0].first, cir = vec[0].second;
cout << square << " " << cir << endl;
return 0;
}