搜索
Intro
搜索差不多也可以说算是一种暴力枚举策略,从初始状态开始,逐渐扩大寻找范围,直到找到答案。但是和直接枚举相比减少了一些无效状态,提高了效率,在数据规模不大时搜索仍然是一种有效的办法。根据搜索的特性可以分为深度优先搜索和广度优先搜索。
深度优先搜索
在枚举某种填空方式时,我们先枚举这个空所有可能的选项,如果有合法的就填下一个选项,然后继续,如果这个空所有选项都不合法,那么就回到上一个空尝试更换选项,继续枚举。这种方式称为回溯算法,常用深度优先搜索来实现。
代码框架
void dfs(int k){//k代表递归层数,或者说要填第几个空
if(所有空都填完了){
判断最优解或记录答案;
return ;
}
for(枚举这个空能填的选项){
if(这个选项是合法的){
记录下这个空(保存现场);
dfs(k+1);
取消这个空(恢复现场);
}
}
}
例题
四阶数独 (枚举排列)
const int cnt = 5;
const int N = 16;
int a[cnt*cnt],ans;
bool row[cnt][cnt],col[cnt][cnt],block[cnt][cnt];
void dfs(int k){
if(k > N){
for(int i = 1 ; i <= N ; i ++ ){
printf("%d",a[i]);
if(i%4 == 0)puts("");
}
puts("");
return;
}
int r = (k-1)/4+1,c = (k-1)%4+1,b = (r - 1)/2*2+(c - 1)/2 + 1;
for(int i = 1 ; i <= 4;i++){
if(!row[r][i] && !col[c][i] && !block[b][i]){
row[r][i] = col[c][i] = block[b][i] = true;
a[k] = i;
dfs(k+1);
row[r][i] = col[c][i] = block[b][i] = false;
}
}
}
P1219 [USACO1.5]八皇后 Checker Challenge (枚举排列)
int path[N],ans,n;
bool col[N],dg[N],udg[N];
void dfs(int k){
if(k>n){
ans ++;
if(ans<=3) {
for(int i = 1 ; i <= n; i ++ ){
printf("%d ",path[i]);
}
cout<<endl;
}
return ;
}
for(int i = 1; i <= k ; i ++){
int ndg = i - k + n - 1;
int nudg = i + k - 1;
if(!col[i] && !dg[ndg] && !udg[nudg]){
path[k] = i;
col[i] = dg[ndg] = udg[nudg] = true;
dfs(k+1);
col[i] = dg[ndg] = udg[nudg] = false;
}
}
}
P2392 kkksc03考前临时抱佛脚 (枚举子集)
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20;
int a[N];
int s[4],maxtime,maxdepth,nowtime,sum;
void dfs(int x){
if(x > maxdepth){
maxtime = max(nowtime,maxtime);
return ;
}
if(nowtime+a[x]<=sum/2){
nowtime += a[x];
dfs(x+1);
nowtime -= a[x];
}
dfs(x+1);
}
int main(){
cin>>s[0]>>s[1]>>s[2]>>s[3];
int ans = 0;
for(int i = 0 ; i < 4; i ++){
sum = 0;
maxdepth = s[i];
nowtime = 0;
maxtime = 0;
for(int j = 1 ; j <= s[i] ; j ++){
cin>>a[j];
sum += a[j];
}
dfs(1);
ans += (sum - maxtime);
}
cout<<ans<<endl;
return 0;
}
广度优先搜索
广度优先搜索的搜索树是一层一层的,他会优先到达离初始状态最近的状态,常见的应用是求边权为1的最短路。
代码框架
q.push(初始状态);//将初始状态入队
while(!q.empty()){
State u = q.front();//取出队首
q.pop();//出队
for(枚举所有可扩展状态)//找到u的所有可达状态v
{
if(是合法的){//v需要满足某些条件,如未访问过,未在队内等
q.push(v);//入队(同时可能需要维护某些必要信息)
}
}
}
例题
P1443 马的遍历
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
const int N = 450;
typedef pair<int,int> pii;
int d[N][N],n,m,x0,y0;
int dx[8] = {2,2,-2,-2,1,1,-1,-1},dy[8] = {1,-1,1,-1,2,-2,2,-2};
int main(){
memset(d,-1,sizeof d);
cin>>n>>m>>x0>>y0;
d[x0][y0] = 0;
queue<pii> q;
q.push({x0,y0});
while(q.size()){
auto t = q.front();
q.pop();
for(int i = 0 ; i < 8 ; i ++){
int x = t.first + dx[i],y = t.second + dy[i];
if(d[x][y] == -1 && x >= 1 && x <= n && y>=1 && y<= m){
q.push({x,y});
d[x][y] = d[t.first][t.second] + 1;
}
}
}
for(int i = 1; i <= n ; i ++){
for(int j = 1 ; j <= m ; j ++){
printf("%-5d",d[i][j]);
}
puts("");
}
return 0;
}
P1135 奇怪的电梯
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 250;
int d[N], dh[2] = {-1,1};
int k[N],n,a,b;
int main(){
memset(d,-1,sizeof d);
cin>>n>>a>>b;
for(int i = 1 ; i <= n ; i ++)cin>>k[i];
queue<int> q;
q.push(a);
d[a] = 0;
while(q.size()){
int t = q.front();
q.pop();
for(int i = 0 ; i < 2 ; i ++){
int h = t + dh[i]*k[t];
if(d[h]==-1 && h >= 1 && h<=n){
d[h] = d[t] + 1;
q.push(h);
}
}
}
cout<<d[b]<<endl;
return 0;
}
P2895 [USACO08FEB]Meteor Shower S
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 350;
int m,death[N][N],map[N][N],ans = 100000;
int dx[4] = {1,-1,0,0},dy[4] = {0,0,1,-1};
struct coord{
int x;
int y;
};
int main(){
memset(map,-1,sizeof map);
memset(death,0x7f,sizeof death);
cin>>m;
for(int i = 0 ; i < m ; i ++){
int a,b,c;
cin>>a>>b>>c;
death[a][b] = min(death[a][b],c);
for(int i = 0 ; i < 4;i++){
int u = a+dx[i],v=b + dy[i];
if(u>=0 && v>=0)
death[u][v] = min(death[u][v],c);
}
}
queue<coord> q;
map[0][0] = 0;
q.push((coord){0,0});
while(q.size()){
auto f = q.front();
q.pop();
int ux = f.x,uy = f.y;
for(int i = 0 ; i < 4;i++){
int fx = ux + dx[i],fy = uy+dy[i];
if(fx>=0 && fy>=0 && map[fx][fy] == -1&&map[ux][uy]+1<death[fx][fy]){
map[fx][fy] = map[ux][uy] + 1;
q.push((coord){fx,fy});
}
}
}
for(int i = 0 ; i < 305 ; i ++){
for(int j = 0 ; j < 305;j++){
if(death[i][j]>1000 && map[i][j]!=-1)
ans = min(ans,map[i][j]);
}
}
if(ans == 100000) cout<<-1<<endl;
else cout<<ans<<endl;
return 0;
}
Summary
搜索都是寻找目标解,dfs 常常寻找的是字典序最小的,而 bfs 寻找步骤最少的,要根据需要来选择算法。

浙公网安备 33010602011771号