【第二学期第二周学习记录】

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都可以写()

  1. 先把每个点输进去,并打好标记(好像是一句废话/bushi
  2. 开始遍历,如果这个点的坐标是被标记的点,就从这个点开始搜索,知道把可以连起来的地方都取消标记,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 1xn400 1 ≤ y ≤ m ≤ 400 1 \leq y \leq m \leq 400 1ym400

解题思路

  • 刚开始想的是把每个点都单独做一遍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) (1N1000)

以下 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 年第九届省赛

解题思路

  1. 刚开始的想法特别朴素,想着拿全球变暖前后的岛屿数量做差就好,然而事实上这样并不靠谱,因为对于某些奇形怪状的岛屿,它在全球变暖后可能还会从一个变为几个(所以这是错误的())
  2. 正确的方法是对他进行标记,海洋一类,会被淹没的土地一类,不会被淹没的土地一类。
  3. 遍历,如果不是海洋就开始搜索;发现这一片联通的地带还有未被淹没的土地就打上标记;没被打标机就说明全淹没完了,就可以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(2n1000) m ( 0 ≤ m ≤ 2000 ) m(0 \le m \le 2000) m(0m2000),分别代表站点数,通道数。

接下来 m m m 行,每行两个整数 u , v ( 1 ≤ u , v ≤ n , u ≠ v ) u,v(1 \le u,v \le n,u\neq v) u,v(1u,vn,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 1n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 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 1n9

思路

  • 混到代码里当备注了嘿嘿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 AB 定义如下:

  • A ⊕ B A \oplus B AB 的二进制表示中, 2 k 2^k 2k 位( k ≥ 0 k \geq 0 k0)的值为 1 1 1,当且仅当 A A A B B B 2 k 2^k 2k 位上的值不同;否则为 0 0 0

例如, 3 ⊕ 5 = 6 3 \oplus 5 = 6 35=6(二进制表示为 011 ⊕ 101 = 110 011 \oplus 101 = 110 011101=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) (((p1p2)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 2N10
  • N − 1 ≤ M ≤ N ( N − 1 ) 2 N - 1 \leq M \leq \frac{N(N-1)}{2} N1M2N(N1)
  • 1 ≤ u i < v i ≤ N 1 \leq u_i < v_i \leq N 1ui<viN
  • 0 ≤ w i < 2 60 0 \leq w_i < 2^{60} 0wi<260
  • 输入的图是简单连通无向图
  • 输入中的所有值均为整数
样例解释 1

从顶点 1 1 1 到顶点 4 4 4 存在以下两条简单路径:

  1. 顶点 1 1 1 → 顶点 2 2 2 → 顶点 4 4 4
    路径上的边标签总异或值为 6 6 6
  2. 顶点 1 1 1 → 顶点 3 3 3 → 顶点 4 4 4
    路径上的边标签总异或值为 3 3 3
    因此,最小值为 3 3 3

翻译由 DeepSeek R1 完成

思路

  1. 首先肯定是要用dfs的;
  2. 然后的问题就是怎么统计最小值:开全局变量ans;dfs的时候额外传一个参数作为当前的异或值;每一支到头以后用这个异或值和ans取min;
  3. 非常重要的一点:记得开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;
  }  

最后特别鸣谢:温柔可爱幼稚善良的石宇学长/耶

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值