QAQ这周主要在看搜索,所以就分享几道搜索的题好了QAQ
P1596 [USACO10OCT] Lake Counting S
题目描述
Due to recent rains, water has pooled in various places in Farmer John’s field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100) squares. Each square contains either water (‘W’) or dry land (‘.’). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors. Given a diagram of Farmer John’s field, determine how many ponds he has.
题意翻译
由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 N×M(1≤N≤100,1≤M≤100) 的网格图表示。每个网格中有水(W) 或是旱地(.)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。
输入第 1 行:两个空格隔开的整数:N 和 M。
第 2 行到第 N+1 行:每行 M 个字符,每个字符是 W 或 .,它们表示网格图中的一排。字符之间没有空格。
输出一行,表示水坑的数量。
输入格式
Line 1: Two space-separated integers: N and M * Lines 2…N+1: M characters per line representing one row of Farmer John’s field. Each character is either ‘W’ or ‘.’. The characters do not have spaces between them.
输出格式
Line 1: The number of ponds in Farmer John’s field.
输入输出样例 #1
输入 #1
10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.
输出 #1
3
说明/提示
OUTPUT DETAILS: There are three ponds: one in the upper left, one in the lower left, and one along the right side.
解题思路
这道题拿bfs和dfs都可以写()
- 先把每个点输进去,并打好标记(好像是一句废话/bushi
- 开始遍历,如果这个点的坐标是被标记的点,就从这个点开始搜索,知道把可以连起来的地方都取消标记,ans++;
代码:bfs
#include <bits/stdc++.h>
using namespace std;
const int N=102;
int vis[N][N];
char a[N][N];
int n,m,ans=0;
struct pos{
int xxx,yyy;
}cur,nex;
queue<pos> q;
int dx[8]={-1,-1,-1,0,1,1,1,0};
int dy[8]={-1,0,1,1,1,0,-1,-1};
void bfs(int x,int y){
cur.xxx=x;cur.yyy=y;
q.push(cur);
vis[x][y]=1;
while(q.size()){
cur=q.front();
q.pop();
for(int i=0;i<8;i++){
nex.xxx=cur.xxx+dx[i];
nex.yyy=cur.yyy+dy[i];
if(nex.xxx<=n&&nex.xxx>=1&&nex.yyy<=m&&nex.yyy>=1&&vis[nex.xxx][nex.yyy]==0){
vis[nex.xxx][nex.yyy]=1;
q.push(nex);
}
}
}
return ;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
if(a[i][j]=='.') vis[i][j]=1;
else vis[i][j]=0;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==0){
bfs(i,j);ans++;
}
}
}
cout<<ans;
return 0;
}
``
代码:dfs
#include <bits/stdc++.h>
using namespace std;
const int N=102;
int vis[N][N];
char a[N][N];
int n,m,ans=0;
int dx[8]={-1,-1,-1,0,1,1,1,0};
int dy[8]={-1,0,1,1,1,0,-1,-1};
void dfs(int x,int y){
vis[x][y]=1;
for(int i=0;i<8;i++){
int xx=x+dx[i];
int yy=y+dy[i];
if(xx<=n&&xx>=1&&yy<=m&&yy>=1&&vis[xx][yy]==0){
vis[xx][yy]=1;
dfs(xx,yy);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
if(a[i][j]=='.') vis[i][j]=1;
else vis[i][j]=0;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==0){
dfs(i,j);ans++;
}
}
}
cout<<ans;
return 0;
}
BFS的一些题目
P1443 马的遍历
题目描述
有一个 n × m n \times m n×m 的棋盘,在某个点 ( x , y ) (x, y) (x,y) 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。
输入格式
输入只有一行四个整数,分别为 n , m , x , y n, m, x, y n,m,x,y。
输出格式
一个 n × m n \times m n×m 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 − 1 -1 −1)。
输入输出样例 #1
输入 #1
3 3 1 1
输出 #1
0 3 2
3 -1 1
2 1 4
说明/提示
数据规模与约定
对于全部的测试点,保证 1 ≤ x ≤ n ≤ 400 1 \leq x \leq n \leq 400 1≤x≤n≤400, 1 ≤ y ≤ m ≤ 400 1 \leq y \leq m \leq 400 1≤y≤m≤400。
解题思路
-
刚开始想的是把每个点都单独做一遍bfs来算需要走多少步()然后毫无意外的超时了QAQ
-
但其实只要搜索一遍就可以了()只要让他把所有点都搜一遍,记录它到达时的步数就可以了(每个点只会到达一次,所以达到步数就是第一次到达步数)
代码
可以直接用vis(用来做标记的数组)计数;
#include <bits/stdc++.h>
using namespace std;
const int N=403;
int n,m,a,b;
int v[N][N];
struct pos{
int xx,yy;
}cur,nex;
queue<pos> q;
int dx[8]={-1,-2,-2,-1,1,2,2,1};
int dy[8]={-2,-1,1,2,2,1,-1,-2};
int vis[N][N];
void bfs()
{
cur.xx=a;cur.yy=b;
q.push(cur);
vis[a][b]=0;
while(q.size()){
cur=q.front();
q.pop();
for(int i=0;i<8;i++){
nex.xx=cur.xx+dx[i];
nex.yy=cur.yy+dy[i];
if(nex.xx<=n&&nex.yy<=m&&nex.xx>=1&&nex.yy>=1&&vis[nex.xx][nex.yy]==0){
q.push(nex);
vis[nex.xx][nex.yy]=vis[cur.xx][cur.yy]+1;
}
}
}
}
int main()
{
cin>>n>>m>>a>>b;
bfs();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==0&&!(i==a&&j==b)) vis[i][j]=-1;
vis[a][b]=0;
printf("%-5d",vis[i][j]);//带-表示靠左边输出;
}
cout<<endl;
}
return 0;
}
当然,如果你实在觉得别扭的话,也可以专门开一个数组记录每一个的步数()
(虽然但是,我真的感觉这样更好理解()QAQ)
#include <bits/stdc++.h>
using namespace std;
const int N=403;
int n,m,a,b;
struct pos{
int xx,yy,step;
}cur,nex;
queue<pos> q;
int dx[8]={-1,-2,-2,-1,1,2,2,1};
int dy[8]={-2,-1,1,2,2,1,-1,-2};
int vis[N][N],stp[N][N];
void bfs()
{
cur.xx=a;cur.yy=b;
cur.step=0;
q.push(cur);
vis[a][b]=1;
stp[a][b]=0;
while(q.size()){
cur=q.front();
q.pop();
for(int i=0;i<8;i++){
nex.xx=cur.xx+dx[i];
nex.yy=cur.yy+dy[i];
nex.step=cur.step+1;
if(nex.xx<=n&&nex.yy<=m&&nex.xx>=1&&nex.yy>=1&&vis[nex.xx][nex.yy]==0){
q.push(nex);
vis[nex.xx][nex.yy]=1;
stp[nex.xx][nex.yy]=nex.step;
}
}
}
}
int main()
{
cin>>n>>m>>a>>b;
bfs();
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(vis[i][j]==0&&!(i==a&&j==b)) stp[i][j]=-1;
stp[a][b]=0;
printf("%-5d",stp[i][j]);
}
cout<<endl;
}
}
P8662 [蓝桥杯 2018 省 AB] 全球变暖
题目描述
你有一张某海域
N
×
N
N \times N
N×N 像素的照片,.
表示海洋、 #
表示陆地,如下所示:
.......
.##....
.##....
....##.
..####.
...###.
.......
其中 “上下左右” 四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有 2 2 2 座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。
例如上图中的海域未来会变成如下样子:
.......
.......
.......
.......
....#..
.......
.......
请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。
输入格式
第一行包含一个整数 N N N。 ( 1 ≤ N ≤ 1000 ) (1 \le N \le 1000) (1≤N≤1000)。
以下 N N N 行 N N N 列代表一张海域照片。
照片保证第 1 1 1 行、第 1 1 1 列、第 N N N 行、第 N N N 列的像素都是海洋。
输出格式
一个整数表示答案。
输入输出样例 #1
输入 #1
7
.......
.##....
.##....
....##.
..####.
...###.
.......
输出 #1
1
说明/提示
时限 1 秒, 256M。蓝桥杯 2018 年第九届省赛
解题思路
- 刚开始的想法特别朴素,想着拿全球变暖前后的岛屿数量做差就好,然而事实上这样并不靠谱,因为对于某些奇形怪状的岛屿,它在全球变暖后可能还会从一个变为几个(所以这是错误的())
- 正确的方法是对他进行标记,海洋一类,会被淹没的土地一类,不会被淹没的土地一类。
- 遍历,如果不是海洋就开始搜索;发现这一片联通的地带还有未被淹没的土地就打上标记;没被打标机就说明全淹没完了,就可以ans++;
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e3+14;
int n;
char c[N][N];
struct pos{int xx,yy;}cur,nex;
queue<pos> q;
int vis[N][N];
int x[4]={1,0,-1,0};
int y[4]={0,1,0,-1};
int main()
{
cin>>n;
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>c[i][j];
if(c[i][j]=='.') vis[i][j]=0;//海洋是0;
else if(c[i][j]=='#') vis[i][j]=1;//刚开始土地全部是1;
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(c[i][j]=='.'){
for(int k=0;k<4;k++){
int xi=i+x[k];
int yj=j+y[k];
if(xi<=n&&xi>=1&&yj<=n&&yj>=1&&c[xi][yj]=='#')
vis[xi][yj]=2;//把会变成海洋的土地变成2;
}
}
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(vis[i][j]==2){
int f=0;//拿来做标记判断有没有全部被淹没;
cur.xx=i;cur.yy=j;
q.push(cur);
vis[i][j]=0;
while(q.size()!=0){
cur=q.front();
q.pop();
for(int k=0;k<4;k++){
nex.xx=cur.xx+x[k];
nex.yy=cur.yy+y[k];
if(nex.xx<=n&&nex.yy<=n&&nex.xx>=1&&nex.yy>=1&&vis[nex.xx][nex.yy]!=0){
if(vis[nex.xx][nex.yy]==1) f=1;//没有全部被淹没,ans不++;
vis[nex.xx][nex.yy]=0;
q.push(nex);
}
}
}
if(f==0) ans++;//全部被淹没,ans++;
}
}
}
cout<<ans;
return 0;
}
DFS的一些题目
P8604 [蓝桥杯 2013 国 C] 危险系数
题目背景
抗日战争时期,冀中平原的地道战曾发挥重要作用。
题目描述
地道的多个站点间有通道连接,形成了庞大的网络。但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系。
我们来定义一个危险系数 D F ( x , y ) DF(x,y) DF(x,y):
对于两个站点 x x x 和 y ( x ≠ y ) , y(x\neq y), y(x=y), 如果能找到一个站点 z z z,当 z z z 被敌人破坏后, x x x 和 y y y 不连通,那么我们称 z z z 为关于 x , y x,y x,y 的关键点。相应的,对于任意一对站点 x x x 和 y y y,危险系数 D F ( x , y ) DF(x,y) DF(x,y) 就表示为这两点之间的关键点个数。
本题的任务是:已知网络结构,求两站点之间的危险系数。
输入格式
输入数据第一行包含 2 2 2 个整数 n ( 2 ≤ n ≤ 1000 ) n(2 \le n \le 1000) n(2≤n≤1000), m ( 0 ≤ m ≤ 2000 ) m(0 \le m \le 2000) m(0≤m≤2000),分别代表站点数,通道数。
接下来 m m m 行,每行两个整数 u , v ( 1 ≤ u , v ≤ n , u ≠ v ) u,v(1 \le u,v \le n,u\neq v) u,v(1≤u,v≤n,u=v) 代表一条通道。
最后 1 1 1 行,两个数 u , v u,v u,v,代表询问两点之间的危险系数 D F ( u , v ) DF(u,v) DF(u,v)。
输出格式
一个整数,如果询问的两点不连通则输出 − 1 -1 −1。
输入输出样例 #1
输入 #1
7 6
1 3
2 3
3 4
3 5
4 5
5 6
1 6
输出 #1
2
说明/提示
时限 1 秒, 64M。蓝桥杯 2013 年第四届国赛
解题思路
- 如果一个站点,一旦失去之后,u和v无论如何都无法联系,那么这个站点就是关键点;
- 所以我们可以循环遍历除了u和v之外的站点,如果标记这个点不能走之后,进行搜索,从u拼尽全力也无法到达v,那么ans++;
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1005;
int vis[N];
vector<int> e[N];
int n,m;
int ans=0;
void dfs(int x){
vis[x]=1;
for(auto i:e[x]){
if(vis[i]==1) continue;
vis[i]=1;
dfs(i);
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
int x,y;//我这里拿x,y代替题目中的u,v了
cin>>x>>y;
dfs(x);
if(vis[y]==0){
cout<<"-1";
return 0;
}
for(int i=1;i<=n;i++){
if(i==x||i==y) continue;
for(int j=1;j<=n;j++){
vis[j]=0;//每次循环之前要记得把用来标记的数组都变成0;
}
vis[i]=1;
dfs(x);//试试从x能不能找到y,可以就ans++;
if(vis[y]==0) ans++;
}
cout<<ans;
return 0;
}
P1706 全排列问题
题目描述
按照字典序输出自然数 1 1 1 到 n n n 所有不重复的排列,即 n n n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。
输入格式
一个整数 n n n。
输出格式
由 1 ∼ n 1 \sim n 1∼n 组成的所有不重复的数字序列,每行一个序列。
每个数字保留 5 5 5 个场宽。
输入输出样例 #1
输入 #1
3
输出 #1
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
说明/提示
1 ≤ n ≤ 9 1 \leq n \leq 9 1≤n≤9。
思路
- 混到代码里当备注了嘿嘿QWQ/乖巧.jdp;
代码
#include <bits/stdc++.h>
using namespace std;
const int N=12;
int vis[N];
int a[N];
int n;
//n个数全排列;相当于n个人坐n个位子
void dfs(int i)//i可以理解成下一个被安排的椅子;
{
if(i>n){
//相当于所有椅子都安排完了,可以输出了;
for(int j=1;j<=n;j++) printf("%5d",a[j]);
cout<<"\n";
return ;
}
for(int j=1;j<=n;j++){//j可以理解成每个人;
if(vis[j]==0){
//没被标记过说明这个人还没找到椅子,那么在i号椅子上可以坐下
a[i]=j;
vis[j]=1;//有椅子坐了就标记;
dfs(i+1);
vis[j]=0;
}
}
}
int main()
{
cin>>n;
dfs(1);//每次都从第一个椅子开始选人坐;
return 0;
}
AT_abc396_d [ABC396D] Minimum XOR Path
题目描述
给定一个简单连通无向图,包含 N N N 个顶点(编号为 1 1 1 至 N N N)和 M M M 条边(编号为 1 1 1 至 M M M)。边 i i i 连接顶点 u i u_i ui 和 v i v_i vi,并带有标签 w i w_i wi。
请找出从顶点 1 1 1 到顶点 N N N 的所有简单路径(不重复经过顶点的路径)中,路径上所有边标签的总异或值的最小可能值。
关于异或(XOR)的定义:
对于非负整数
A
A
A 和
B
B
B,它们的异或
A
⊕
B
A \oplus B
A⊕B 定义如下:
- A ⊕ B A \oplus B A⊕B 的二进制表示中, 2 k 2^k 2k 位( k ≥ 0 k \geq 0 k≥0)的值为 1 1 1,当且仅当 A A A 和 B B B 在 2 k 2^k 2k 位上的值不同;否则为 0 0 0。
例如,
3
⊕
5
=
6
3 \oplus 5 = 6
3⊕5=6(二进制表示为
011
⊕
101
=
110
011 \oplus 101 = 110
011⊕101=110)。
对于
k
k
k 个整数
p
1
,
…
,
p
k
p_1, \dots, p_k
p1,…,pk 的异或,定义为
(
⋯
(
(
p
1
⊕
p
2
)
⊕
p
3
)
⊕
⋯
⊕
p
k
)
(\cdots ((p_1 \oplus p_2) \oplus p_3) \oplus \cdots \oplus p_k)
(⋯((p1⊕p2)⊕p3)⊕⋯⊕pk),且其值与运算顺序无关。
输入格式
输入通过标准输入给出,格式如下:
N N N M M M
u 1 u_1 u1 v 1 v_1 v1 w 1 w_1 w1
u 2 u_2 u2 v 2 v_2 v2 w 2 w_2 w2
⋮ \vdots ⋮
u M u_M uM v M v_M vM w M w_M wM
输出格式
输出答案。
输入输出样例 #1
输入 #1
4 4
1 2 3
2 4 5
1 3 4
3 4 7
输出 #1
3
输入输出样例 #2
输入 #2
4 3
1 2 1
2 3 2
3 4 4
输出 #2
7
输入输出样例 #3
输入 #3
7 10
1 2 726259430069220777
1 4 988687862609183408
1 5 298079271598409137
1 6 920499328385871537
1 7 763940148194103497
2 4 382710956291350101
3 4 770341659133285654
3 5 422036395078103425
3 6 472678770470637382
5 7 938201660808593198
输出 #3
186751192333709144
说明/提示
约束条件
- 2 ≤ N ≤ 10 2 \leq N \leq 10 2≤N≤10
- N − 1 ≤ M ≤ N ( N − 1 ) 2 N - 1 \leq M \leq \frac{N(N-1)}{2} N−1≤M≤2N(N−1)
- 1 ≤ u i < v i ≤ N 1 \leq u_i < v_i \leq N 1≤ui<vi≤N
- 0 ≤ w i < 2 60 0 \leq w_i < 2^{60} 0≤wi<260
- 输入的图是简单连通无向图
- 输入中的所有值均为整数
样例解释 1
从顶点 1 1 1 到顶点 4 4 4 存在以下两条简单路径:
- 顶点
1
1
1 → 顶点
2
2
2 → 顶点
4
4
4
路径上的边标签总异或值为 6 6 6。 - 顶点
1
1
1 → 顶点
3
3
3 → 顶点
4
4
4
路径上的边标签总异或值为 3 3 3。
因此,最小值为 3 3 3。
翻译由 DeepSeek R1 完成
思路
- 首先肯定是要用dfs的;
- 然后的问题就是怎么统计最小值:开全局变量ans;dfs的时候额外传一个参数作为当前的异或值;每一支到头以后用这个异或值和ans取min;
- 非常重要的一点:记得开long long!!!ans最好开到2e18!!!
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=50;
int vis[N];
vector<int> e[15];
int a[11][11];
int ans=2e18;
int n,m;
void dfs(int i,int XOR){
vis[i]=1;
if(i==n){
ans=min(ans,XOR);return ;
}
for(auto j:e[i]){
if(vis[j]==1) continue;
vis[j]=1;
dfs(j,XOR^a[i][j]);
vis[j]=0;
}
}
signed main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);
// cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,k;
cin>>u>>v>>k;
e[u].push_back(v);
e[v].push_back(u);
a[u][v]=k;
a[v][u]=k;
}
dfs(1,0);
cout<<ans;
return 0;
}
最后特别鸣谢:温柔可爱幼稚善良的石宇学长/耶