!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

题目:

1.迷宫——洛谷搜索题单1605

2.马的遍历——洛谷搜索题单1443

3.填涂颜色——洛谷搜索题单1162

4.棋盘问题——百练1321

5.马走日——百练4123

6.红与黑——百练2816

7.奇怪的电梯——洛谷搜索题单1135

8.迷宫问题——百练4127

9.Meteor Shower S——洛谷搜索题单2895

10. Corn Maze S——洛谷搜索题单1825

11.八皇后 Checker Challenge——洛谷搜索题单1219

12.单词接龙——洛谷搜索题单1019

本文主要目的是自我记录🤪🌹

搜索

搜索主要划分为  深度优先搜索(dfs)&&  广度优先搜索(bfs),可分,那两者肯定有区别,先说说深搜。

深搜:

&1(搜索方向):主打一个一条路走到黑,不撞南墙不回头,到尾后回退到最近一个“分岔口”

&2(应用方向):暴力搜索( 没有单调性的最大值,最小值),能实现的方案数等等等等

&3(模板):

void dfs(达到目的前的某一步)
{
	if(达到目的) return ;
	(剪枝)
	for(枚举所有可能解)
	{
		if(不满足) continue; 
		mark; 
		dfs(达到目的前的某一步+1);
		取消mark;	
	}
}

广搜:

&1(搜索方向):多路同时进行——看图说话(狗头)

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

&2(应用方向):因为多路同时进行的原因,可以用于搜索最短路,也可以用于遍历等等等等

&3(模板):

#include//头文件 
int main()
{
	queue q;
	q.push(首元素);//向队列增加元素 
	while(q.size())
	{
		p=q.front();//获取队列第一个 
		q.pop();//删除队列第一个 
		for(枚举所有可能解)
		{
			if(不满足) continue;
			mark;
			q.push(当前元素); 
		}
	}
}

前置

如果是需要上下左右走的,而小伙伴们不知道怎么写,可以参考如下代码

(ab约束依照题目)

int xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
……
for(i=0;i<4;i++)
{
	a=x+xi[i];
	b=y+yj[i];
	if(a<1||bn||b>m||map[a][b]==1) continue;
    ……
}

1.迷宫——洛谷搜索题单1605

1.迷宫——洛谷搜索题单1605

思路:dfs模板题吧?暴力搜索——走过就打1,走完就回退,回退取消1

代码:

#include
using namespace std;

int n,m,ei,ej,ans,map[10][10],xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
void dfs(int x,int y)
{
	int i,a,b;
	if(x==ei&&y==ej)
	{
		ans++;
		return ;
	}
	for(i=0;i<4;i++)
	{
		a=x+xi[i];
		b=y+yj[i];
		if(a<1||bn||b>m||map[a][b]==1) continue;
		map[a][b]=1;
		dfs(a,b);
		map[a][b]=0;
	}
}
int main()//1605——迷宫 
{
	int i,j,k,t,bi,bj;
	cin>>n>>m>>t;
	cin>>bi>>bj>>ei>>ej;
	map[bi][bj]=1;
	for(k=0;k>i>>j;
		map[i][j]=1;
	}
	dfs(bi,bj);
	cout< 

2.马的遍历——洛谷搜索题单1443

2.马的遍历——洛谷搜索题单1443

思路:能跳到的点为上一个点所跳步数+1,类似递推桶,基于广搜原理的题,多路同时进行,跳跳跳(狗头)

&1,初始化地图

&2,多路同时进行,能跳到的点为上一个点所跳步数+1,“打上标记”

memset函数:将 某一块内存 中的全部元素 设置成 指定值

memset(变量名,指定值,大小(可用sizeof函数))

代码:

#include
#include
#include
using namespace std;

int c[405][405],xi[8]={-1,-2,-2,-1,1,2,2,1},yj[8]={-2,-1,1,2,2,1,-1,-2};
struct point{
	int a,b;
}p;
int main()//1443——马的遍历 
{
	int n,m,x,y,px,py,i,j;
	queue q;
	memset(c,-1,sizeof(c));//头文件csrting 
	cin>>n>>m>>x>>y;
	c[x][y]=0;
	q.push(point{x,y});
	while(q.size())
	{
		p=q.front();
		q.pop();
		for(i=0;i<8;i++)
		{
			px=xi[i]+p.a;
			py=yj[i]+p.b;
			if(pxn||pym||c[px][py]!=-1) continue;
			c[px][py]=c[p.a][p.b]+1;
			q.push(point{px,py});
		}
	}
	for(i=1;i<=n;i++)
	{
		for(j=1;j<=m;j++) cout<<c[i][j]<<"    ";//题目需求 
		cout<<endl;
	}
	return 0;
}

3.填涂颜色——洛谷搜索题单1162

3.填涂颜色——洛谷搜索题单1162

思路:遍历地图!广搜!

&1,因为1内的0不能被搜到,所以可用一个假地图来  “存入”  地图1外全部0信息

&2,为了防止1在边界导致广搜不到0,在地图外再加一圈0

&3,输出根据真假地图来输出

代码:

#include
#include
using namespace std;

int map[35][35],fmap[35][35],xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
struct point{
	int a,b;
}p;
int main()//1162
{
	int i,j,n,x,y;
	queue q;
	cin>>n;
	for(i=1;i<n+1;i++)
	{
		for(j=1;j>map[i][j];
	}
	fmap[0][0]=1;
	q.push(point{0,0});
	while(q.size())
	{
		p=q.front();
		q.pop();
		for(i=0;i<4;i++)
		{
			x=p.a+xi[i];
			y=p.b+yj[i];
			if(xn+1||yn+1||fmap[x][y]==1) continue;
			if(map[x][y]==0&&fmap[x][y]==0)
			{
				fmap[x][y]=1;
				q.push(point{x,y});
			}
		}
	}
	for(i=1;i<n+1;i++)
	{
		for(j=1;j<n+1;j++)
		{
			if(map[i][j]==0&&fmap[i][j]==1) cout<<"0 ";
			else if(map[i][j]==1&&fmap[i][j]==0) cout<<"1 ";
			else cout<<"2 ";
		}
		cout<<endl;
	}
	return 0;
}

4.棋盘问题——百练1321

4.棋盘问题——百练1321

思路:其实这道题望眼欲穿,这不就是排列组合吗?直接上  C(可塞棋子位数)^ (棋子数),不幸的是wa了,这题需要高精度……

言归正传……方案数——深搜,但是这是一道好题,与深搜水题不同——棋子不一定每行都有,这该怎么办?莫急,转念一想,就是相当于多了暴搜某些行不枚举罢了

&1,使用b[j]表示这一列都被标记过,灵活变通(狗头)

&2,暴力枚举每一行,能塞就塞,打标记,棋子摆放完,去除标记

&3,某些行不枚举……

那怎么实现?其实很简单

void dfs(int x,int last)
{
	int j;
	if(last==0)
	{
		ans++;
		return ;
	}
	if(x>n) return ;//越界 
	for(j=1;j<=n;j++)
	{
		if(b[j]||a[x][j]) continue;
		b[j]=1;
		dfs(x+1,last-1);
		b[j]=0;
	}
}

这是我们每一行都搜的情况,我们只需要加入dfs(x+1,last);即可。这句代码表示这行我不搜

代码:

#include
using namespace std;

int n,k,ans,a[10][10],b[10];
void dfs(int x,int last)
{
	int j;
	if(last==0)
	{
		ans++;
		return ;
	}
	if(x>n) return ;//越界 
	for(j=1;j>n>>k;
		if(n==-1&&k==-1) break;
		for(i=1;i<=n;i++)
		{
			for(j=1;j>ch;
				if(ch=='#') a[i][j]=0;
				else a[i][j]=1;
			}
		}
		for(i=1;i<=n;i++) b[i]=0;
		ans=0;
		dfs(1,k);
		cout< 

5.马走日——百练4123

5.马走日——百练4123

思路:又是一道深搜模板题!方案数?深搜!只不过判断条件变成了    能跳的次数是否等于n*m

可能有的小伙伴会问,如果cnt小于n*m那是不是出不来函数了?其实不然,因为这种情况就是说明8个方向都continue了,进不去下一个dfs了

值得一提的是,方向坐标不能设置成全局变量(捂脸)

代码:

#include
#include
using namespace std;

int n,m,sum,map[15][15],xi[8]={-1,-2,-2,-1,1,2,2,1},yj[8]={-2,-1,1,2,2,1,-1,-2};
void dfs(int x,int y,int cnt)
{
	int i,a,b;//a,b不能是全局变量 
	if(cnt==n*m)
	{
		sum++;
		return ;	
	}
	for(i=0;i<8;i++)
	{
		a=x+xi[i];
		b=y+yj[i];
		if(a=n||b=m||map[a][b]==1) continue;
		map[a][b]=1;
		dfs(a,b,cnt+1);
		map[a][b]=0;
	}
}
int main()
{
	int t,x0,y0,i,j;
	cin>>t;
	while(t--)
	{
		memset(map,0,sizeof(map));
		cin>>n>>m>>x0>>y0;
		map[x0][y0]=1;
		sum=0;
		dfs(x0,y0,1);
		cout<<sum<<endl;
	}
	return 0;
}

6.红与黑——百练2816

6.红与黑——百练2816

思路:深搜广搜都行,每个可以走的块都加1即可。

用dfs写不需要取消标记,因为我们现在是遍历整张地图!

我提供的代码可能有的小伙伴会问ans怎么会加啊?dfs里不就只有一个ans+=嵌套的dfs吗?奥秘蕴藏在return中,每一个能走的dfs本身的ans就是1。最后一个能遍历到的点的ans是1,它就会回退,那么它的回退dfs就会加上它的ans(也就是1),然后退退退,退回最终所有能遍历点的个数

代码:

#include
#include
using namespace std;

int n,m,map[25][25],xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
int dfs(int x,int y)
{
	int a,b,i,ans=1;
	for(i=0;in||y>m||x<1||y>m>>n;
		if(m==0&&n==0) break;
		memset(map,1,sizeof(map));
		for(i=1;i<=n;i++)
		{
			for(j=1;j>ch;
				if(ch=='.') map[i][j]=0;
				else if(ch=='@')
				{
					x0=i;
					y0=j;
				}
			}
		}
		cout<<dfs(x0,y0)<<endl;
	} 
	return 0;
}

7.奇怪的电梯——洛谷搜索题单1135

7.奇怪的电梯——洛谷搜索题单1135

思路:本题答案不具有单调性,贪心不知道能不能写。暴搜吧,放弃了,交给计算机……

但是!本题需要剪枝,否则就要tle了!

那该怎么剪?经过  反证法  我们发现   能通往答案的每一层楼,它的步数都是最小的!所以剪这部分

代码:

#include
using namespace std;

int s[205],fs[205],n,a,b,ans=1e9;
void dfs(int change,int solid,int fans)
{
	if(changen||fans>=ans||fans>=fs[change]) return ;
	if(change==solid)
	{
		ans=min(fans,ans);
		return ;
	}
	fs[change]=min(fans,fs[change]);
	dfs(change+s[change],solid,fans+1);
	dfs(change-s[change],solid,fans+1);
}
int main()//1135
{
	int i;
	cin>>n>>a>>b;
	for(i=1;i>s[i];
		fs[i]=1e9;
	}
	dfs(a,b,0);
	if(ans!=1e9) cout< 

8.迷宫问题——百练4127

思路:这题是道Boss题。难就难在怎么输出坐标?

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

以下是广搜路线

我们发现这个坐标也就是树,它可以子推亲,但是不能亲推子

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

从4,4->0,0

结构体具有这样的性质,所以我们用  子“套”亲

struct point{
	int 亲.x,亲.y;
}mark[子.x][子.y];

子坐标内存的是子的亲坐标,所以我们可以用递归一层一层的 套 出来,但是0,0没有亲!所以单独输出

代码:

#include
#include
using namespace std;

int map[10][10],xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
struct point{
	int x,y;
}p,mark[10][10];
void put(int xa,int yb)
{
	if(xa==0&&yb==0) return ;
	p.x=mark[xa][yb].x;
	p.y=mark[xa][yb].y;
	put(p.x,p.y);
	cout<<"("<<xa<<", "<<yb<<")"<<endl;
}
int main()
{
	int i,j,a,b;
	queue q;
	for(i=0;i<5;i++)
	{
		for(j=0;j>map[i][j];
	}
	q.push(point{0,0});
	map[0][0]=1;
	while(q.size())
	{
		p=q.front();
		q.pop();
		for(i=0;i<4;i++)
		{
			a=p.x+xi[i];
			b=p.y+yj[i];
			if(a=5||b=5||map[a][b]==1) continue;
			map[a][b]=1;
			mark[a][b].x=p.x;
			mark[a][b].y=p.y;
			q.push(point{a,b});
		}
	}
	cout<<"(0, 0)"<<endl;
	put(4,4);
	return 0;
}

9.Meteor Shower S——洛谷搜索题单2895

9.Meteor Shower S——洛谷搜索题单2895

思路: 广搜。对所有点的时间设置-1,然后存储这个点被砸的时间,人走当前能走的点(即人走到这个点的时间小于被砸的时间或者-1)/*现在的-1表示这个点没有被砸*/,走到-1即可。

注意:&1,赋值不能越界(放这个图就懂了),因为作者i,j<306RE了

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

&2,memset会将结构体5变量全设置成-1(上文里的全部)

&3,如果数据后有流星时间小于数据前被砸的点的时间,更新

&4,先前我的判定是写成下面的代码,但是wa了

因为   s[dx][dy].t==-1时   也是小于p.step+1   会跳过,就没有正确答案了。想这么写的小伙伴加个&&就好了

if(dx<0||dy<0||s[dx][dy].mark==1||s[dx][dy].t<=p.step+1) continue;
s[dx][dy].step=p.step+1;
s[dx][dy].mark=1;
if(s[dx][dy].t==-1)
{
	cout<<s[dx][dy].step;
	return 0;
}
q.push(s[dx][dy]);

代码:

#include
#include
#include
using namespace std;

int xi[5]={-1,1,0,0,0},yj[5]={0,0,-1,1,0};
struct map{
	int x,y,t,step,mark; 
}p,s[305][305];
int main()//2895
{
	int m,i,j,k,dt,a,b,dx,dy;
	memset(s,-1,sizeof(s));//将结构体5个变量全设成-1 
	queue q;
	for(i=0;i<305;i++)
	{
		for(j=0;j>m;
	for(i=0;i>a>>b>>dt;
		for(k=0;k<5;k++)
		{
			dx=a+xi[k];
			dy=b+yj[k];
			if(dx<0||dydt) s[dx][dy].t=dt;
		}
	}
	s[0][0].mark=1;
	s[0][0].step=0;
	q.push(s[0][0]);
	while(q.size())
	{
		p=q.front();
		q.pop();
		for(i=0;i<4;i++)
		{
			dx=p.x+xi[i];
			dy=p.y+yj[i];
			if(dx<0||dy<0||s[dx][dy].mark==1) continue;
			s[dx][dy].mark=1;
			s[dx][dy].step=p.step+1;
			if(s[dx][dy].t==-1)
			{
				cout<<s[dx][dy].step;
				return 0;
			}
			if(p.step+1<s[dx][dy].t) q.push(s[dx][dy]);
		}
	}
	cout<<"-1";
	return 0;
}

10. Corn Maze S——洛谷搜索题单1825

10. Corn Maze S——洛谷搜索题单1825

思路:广搜!只不过多了个传送门罢了,小场面来的。

&1,用结构体存储两个传送门,遇到其中一个,直接调用另一个

&2,使用传送门step不加,传送门不打标记,原因如下

#########=
####.@.Q..
.....####.
.....####.
###..####.
###Q.####.
####......

这个最短路用了两次传送门,先往右遇到传送门,从传送门出来,再从传送门进去

代码:

#include
#include
using namespace std;

char map[305][305],ch;
int xi[4]={-1,1,0,0},yj[4]={0,0,-1,1};
struct P{
	int x1,y1,x2,y2;
}s[100];
struct point{
	int x,y,step;
}p;
int main()//1825
{
	int n,m,i,j,a,b;
	queue q;
	cin>>n>>m;
	for(i=1;i<=n;i++)
	{
		for(j=1;j>map[i][j];
			ch=map[i][j];
			if(ch>='A'&&ch<='Z')
			{
				if(s[ch].x1==0)
				{
					s[ch].x1=i;
					s[ch].y1=j;
				}
				else
				{
					s[ch].x2=i;
					s[ch].y2=j;
				}
			}
			if(ch=='@')
			{
				q.push(point{i,j,0});
				map[i][j]='#';
			}
		}
	}
	while(q.size())
	{
		p=q.front();
		q.pop();
		for(i=0;i<4;i++)
		{
			a=p.x+xi[i];
			b=p.y+yj[i];
			ch=map[a][b];
			if(an||bm||map[a][b]=='#') continue;
			if(ch=='=')
			{
				cout<<p.step+1;
				return 0;
			}
			if(ch=='.')
			{
				q.push(point{a,b,p.step+1});
				map[a][b]='#';
			}
			if(s[ch].x1==a&&s[ch].y1==b) q.push(point{s[ch].x2,s[ch].y2,p.step+1});
			else q.push(point{s[ch].x1,s[ch].y1,p.step+1});
		}
	}
	return 0;
}

11.八皇后 Checker Challenge——洛谷搜索题单1219

11.八皇后 Checker Challenge——洛谷搜索题单1219

思路:这题与棋盘问题不同,多了对角线限定,但少了棋子限定,方法还是dfs。

那现在就来找找主副对角线上有什么规律(不可能去遍历每一个点来判定的)

z表示一个未知数

我们发现   任一主对角线的 i+j  永远等于一个特定值 ; 而任一副对角线  j-i  也是永远等于一个特定值,因为  j-i  的值会有小于0的情况,所以我们在    j-i的基础上+n

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

代码:

#include
using namespace std;

int n,c[30],ch[30],che[30],a[30],cnt;
void dfs(int i)
{
	int j,z;
	if(i>n)
	{
		cnt++;
		if(cnt<=3)
		{
			for(z=1;z<=n;z++) coutn;
	dfs(1);
	cout<<cnt;
	return 0;
}

12.单词接龙——洛谷搜索题单1019

12.单词接龙——洛谷搜索题单1019

思路:先不论单词拼接,想法肯定是暴力深搜,简便快捷。

我们主要的问题肯定在于单词怎么判定拼接?这里我们需要使用一个叫 substr 的函数,用于比较两个字符串的某部分

主要用法如下  

substr内第一个数字表示截取的位置,第二个表示截取个数,不是终止位置

!搜索(DFS&&BFS)!(校内题解,题目来自洛谷和百练poj)

题目是想比较,我们就取出来比较(注意截取个数不能大于len2)

len1的最后一个位置是len1-1,以此类推往前,而我们的截取数和位置有关系,所以有

if(str1.substr(len1-i,i)==str2.substr(0,i)) return str1+str2.substr(i,len2);

虽然str2.substr(i,len2)的len2大于剩余数量,但是没有影响

string类型是可以直接用+表示连接的

代码:

#include
#include
using namespace std;

string s[25];
int n,ans,mark[25];
string judge(string str1,string str2)
{
int i,len1=str1.size(),len2=str2.size();
for(i=1;i<len1&&ians) ans=str.size();
for(i=0;i>n;
for(i=0;i>s[i];
cin>>c;
for(i=0;i<n;i++)
{
if(s[i][0]==c)
{
mark[i]++;
dfs(s[i]);
mark[i]--;
}
}
cout<

最后

这篇题解涉及大部分洛谷官方搜索题解,下次更洛谷搜索题解就只更剩下部分了(狗头)

感觉这些题都挺好的,可以增加对基础搜索算法的理解(揣摩)

下次再见了🌹

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/fb4a0c569a.html