搜索专题(未完)

搜索做题总结

前言:

因为搜索一直被众OIer称为万能算法——很多题都能用搜索得部分分,所以...所以...嗯,还是重新再刷一遍搜索的题,找找感觉吧!希望在之后的大小比赛考试中能够发挥出来quq


搜索提单——从基础到提高(未完):

  • 基础

-- 洛谷P1451 求细胞个数

-- 洛谷P1141 01迷宫

-- 洛谷P1683 入门 (此“入门”非彼“入门”,虽然都还挺简单的)

-- 洛谷P1157 组合的输出

-- 洛谷P1036 选数

-- 洛谷P1443 马的遍历

-- 洛谷P1746 离开中山路

-- 洛谷P2372 yyy2015c01挑战算周长

(题目有点长..我这个蒟蒻读了两遍题才读懂QAQ但是题很简单)

其实这些题很多都是有相似的地方的,而这些相似的地方就是搜索的基础。我们可以把这里作为突破口,然后清晰思路,更利于编程。

常见的搜索基础类型:“搜索求联通块”、“n个数选k个数”

  • 提高

-- 洛谷P2360 地下城主

(虽然标签是普及-,但是我认为比马的遍历那些题会难一点?大概还是因为我蒻)

-- 洛谷P2298 Mzc和男家丁的游戏

-- 洛谷P1332 血色先锋队

-- 洛谷P1434 滑雪

-- 洛谷P1019 单词接龙

-- 洛谷P1032 字串变换

-- 洛谷P3956 棋盘

(这道题我用广搜没有搞出来,就是太菜,然后使用了WS同学的深搜,这里就推荐一篇他的题解:棋盘 题解

-- 洛谷P1118 [USACO06FEB]Backward Digit Sums G/S


部分题目思路:

  • 基础

-- 求细胞个数、01迷宫、入门、yyy2015c01挑战算周长:“搜索求联通块”

-- 组合的输出、选数:DFS,“n个数选k个数”

-- 马的遍历、离开中山路:BFS求最短距离

  • 提高

-- 地下城主:三维搜索+求最短距离,注意一下数组是先层再行再列(我栽在这1hQAQ),后面给出该题的代码

-- Mzc和男家丁的游戏、血色先锋队:求最短距离

-- 滑雪、单词接龙、字串变换、棋盘、Backward Digit Sums G/S:之后会给出代码&题解


部分题目代码(题解以后再补充qwq):

  • 求细胞个数
#include <bits/stdc++.h>
using namespace std;
int n,m,ans;
char a[105][105];
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

inline void bfs(int x,int y) {
	a[x][y]='0';
	for(register int i=0;i<4;i++) {
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx>n||xx<1||yy>m||yy<1||a[xx][yy]=='0') continue;
		bfs(xx,yy);
	}
}

int main() {
	scanf("%d%d",&n,&m);
	for(register int i=1;i<=n;i++) {
		for(register int j=1;j<=m;j++) {
			cin>>a[i][j];
		}
	}
	for(register int i=1;i<=n;i++) {
		for(register int j=1;j<=m;j++) {
			if(a[i][j]!='0') {
				ans++;
				bfs(i,j);
			}
		}
	}
	printf("%d",ans);
	return 0;
}
  • 选数
#include <bits/stdc++.h>
using namespace std;
int n,r,sum,ans,num[5000001];

inline bool check(int tot) {
	for(register int i=2;i*i<=tot;i++) {
		if(tot%i==0) return false;
	}
	return true;
}

inline void dfs(int now,int k) {
	for(register int i=k;i<=n;i++) {
		sum+=num[i];
		if(now==r) {
			if(check(sum)) ans++;
		}
		else dfs(now+1,i+1);
		sum-=num[i];
	}
}

int main() {
	scanf("%d%d",&n,&r);
	for(register int i=1;i<=n;i++) {
		scanf("%d",&num[i]);
	}
	dfs(1,1);
	printf("%d",ans);
	return 0;
}
  • 马的遍历
#include <bits/stdc++.h>
using namespace std;
int n,m,sx,sy,pd[4005][4005],ans[4005][4005];
int dx[8]={1,2,2,1,-1,-2,-2,-1};
int dy[8]={2,1,-1,-2,-2,-1,1,2};

struct node {
	int x,y,st;
} q[160005];

inline void bfs(int xx,int yy) {
	pd[xx][yy]=1;
	int front=1,tail=1;
	q[front].x=xx;
	q[front].y=yy;
	q[front].st=0;
	while(front<=tail) {
		for(register int i=0;i<8;i++) {
			int xs=q[front].x+dx[i];
			int ys=q[front].y+dy[i];
			if(xs>=1&&xs<=n&&ys>=1&&ys<=m&&pd[xs][ys]==0) {
				tail++;
				q[tail].x=xs;
				q[tail].y=ys;
				q[tail].st=q[front].st+1;
				ans[xs][ys]=q[tail].st;
				pd[xs][ys]=1;
			}
		}
		front++;
	}
}

int main() {
	memset(ans,-1,sizeof(ans));
	scanf("%d%d%d%d",&n,&m,&sx,&sy);
	ans[sx][sy]=0;
	bfs(sx,sy);
	for(register int i=1;i<=n;i++) {
		for(register int j=1;j<=m;j++) {
			printf("%-5d",ans[i][j]);
		}
		puts("");
	}
	return 0;
} 
  • 地下城主
#include <bits/stdc++.h>
using namespace std;
int L,R,C,sx,sy,sh,ex,ey,eh,sum,ans;
char a[31][31][31];
int dx[6]={0,1,0,-1,0,0};
int dy[6]={1,0,-1,0,0,0};
int dh[6]={0,0,0,0,1,-1};

struct node {
	int x,y,h,st;
} q[100001];

inline void bfs(int xx,int yy,int hh) {
	a[hh][xx][yy]='#'; //一定要注意先层再行再列!
	int front=1,tail=1;
	q[front].x=xx;
	q[front].y=yy;
	q[front].h=hh;
	q[front].st=0;
	while(front<=tail) {
		for(register int i=0;i<6;i++) {
			int xs=q[front].x+dx[i];
			int ys=q[front].y+dy[i];
			int hs=q[front].h+dh[i];
			if(xs>=1&&xs<=R&&ys>=1&&ys<=C&&hs>=1&&hs<=L&&a[hs][xs][ys]!='#') {
				tail++;
				q[tail].x=xs;
				q[tail].y=ys;
				q[tail].h=hs;
				q[tail].st=q[front].st+1;
				if(xs==ex&&ys==ey&&hs==eh) {
					printf("Escaped in %d minute(s).",q[tail].st);
					return ;
				}
				a[hs][xs][ys]='#';
			}
		}
		front++;
	}
	puts("Trapped!");
}

int main() {
	scanf("%d%d%d",&L,&R,&C);
	for(register int i=1;i<=L;i++) {
		for(register int j=1;j<=R;j++) {
			for(register int k=1;k<=C;k++) {
				cin>>a[i][j][k];
				if(a[i][j][k]=='S') {
					sx=j;
					sy=k;
					sh=i;
				}
				if(a[i][j][k]=='E') {
					ex=j;
					ey=k;
					eh=i;
				}
			}
		}
	}
	bfs(sx,sy,sh);
	return 0;
}
  • 血色先锋队
#include <bits/stdc++.h>
using namespace std;
int n,m,a,b,pd[5005][5005],ans[5005][5005];
int x,y;
int dx[4]={0,1,0,-1};
int dy[4]={1,0,-1,0};

struct node {
	int x,y,st;
}q[2500005];

inline void bfs(int tot) {
	int front=1,tail=tot;
	while(front<=tail) {
		for(register int i=0;i<4;i++) {
			int xx=q[front].x+dx[i];
			int yy=q[front].y+dy[i];
			if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&pd[xx][yy]==0) {
				pd[xx][yy]=1;
				tail++;
				q[tail].x=xx;
				q[tail].y=yy;
				q[tail].st=q[front].st+1;
				ans[xx][yy]=q[tail].st;
			}
		}
		front++;
	}
}

int main() {
	scanf("%d%d%d%d",&n,&m,&a,&b);
	for(register int i=1;i<=a;i++) {
		scanf("%d%d",&x,&y);
		q[i].x=x;
		q[i].y=y;
		q[i].st=0;
		pd[x][y]=1;
		ans[x][y]=0;
	}
	bfs(a);
	for(register int i=1;i<=b;i++) {
		scanf("%d%d",&x,&y);
		printf("%d
",ans[x][y]);
	}
	return 0;
}
  • 滑雪
#include <bits/stdc++.h>
using namespace  std;
int c,r,sum=0,a[101][101],f[101][101];
int dx[5]={0,0,-1,0,1};
int dy[5]={0,1,0,-1,0};
inline void dfs(int x,int y) {
	f[x][y]=1;
	int tot=0,ans=1;
	for(int i=1;i<=4;i++) {
		int xx=x+dx[i];
		int yy=y+dy[i];
		if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&a[xx][yy]>a[x][y]) {
			if(f[xx][yy]==0) {
				dfs(xx,yy);
			}
			f[x][y]=max(f[x][y],f[xx][yy]+1);
		}
	}
}
int main() {
	cin>>r>>c;
	for(int i=1;i<=r;i++) {
		for(int j=1;j<=c;j++) {
			cin>>a[i][j];
		}
	}
	for(int i=1;i<=r;i++) {
		for(int j=1;j<=c;j++) {
			if(f[i][j]==0) dfs(i,j);
			sum=max(sum,f[i][j]);
		}
	}
	cout<<sum;
	return 0;
}

这道题我们用字母来模拟一下自上而下的合并过程,然后...你就会发现和某某三角(杨辉三角啦)有那么一点点的像,模拟如下:

先来看看n=4的情况
a    b    c    d
a+b   b+c   c+d
a+2b+c  b+2c+d
a+3b+3c+d(sum)

再来看看n=6的情况
a      b      c      d      e     f
a+b     b+c     c+d     d+e     e+f
a+2b+c    b+2c+d    c+2d+e    d+2e+f
a+3b+3c+d   b+3c+3d+e   c+3d+3e+f
a+4b+6c+4d+e  b+4c+6d+4e+f
a+5b+10c+10d+5e+f(sum)

然后我们来看看某某三角
【1】1
【2】1 1
【3】1 2 1
【4】1 3 3 1
【5】1 4 6 4 1
【6】1 5 10 10 5 1
······

将两者进行对比,我们就会发现:
sum表达式中各个项的系数就是杨辉三角中第n行的数字

#include <bits/stdc++.h>
using namespace std;
int n,sum,yh[21][21],a[200010],flag[200010];

inline void dfs(int k,int now) {  
	if(now>sum) return ; //剪枝,如果当前数字和已经大于sum,直接退出 
	if(k>n&&now==sum) {
		for(register int i=1;i<=n;i++) printf("%d ",a[i]);
		exit(0); //找到了就直接结束程序 
	}
	for(register int i=1;i<=n;i++) {
		if(flag[i]==0) { //判重 
			a[k]=i; //存数 
			flag[i]=1;
			dfs(k+1,now+i*yh[n][k]); //核心! 
			flag[i]=0;
		}
	}
}

int main() {
	scanf("%d%d",&n,&sum);
	yh[1][1]=1;
	for(register int i=2;i<=n;i++) { //预处理杨辉三角 
		for(register int j=1;j<=i;j++) {
			yh[i][j]=yh[i-1][j]+yh[i-1][j-1];
		}
	}
	/*for(register int i=1;i<=n;i++) {  打印杨辉三角 
		for(register int j=1;j<=i;j++) {
			cout<<yh[i][j]<<" ";
		}
		cout<<endl;
	}*/
	dfs(1,0);
	return 0;
}

To be continued...


原文地址:https://www.cnblogs.com/Eleven-Qian-Shan/p/13096008.html