搜索分为两种 深度优先搜索 和 广度优先搜索 个人认为解决搜索问题的关键是 找到状态的转移 并且在有限的时间或者空间内找到正确的答案
1.深度优先搜索
dfs是以深度优先的方式 遍历二叉树 一条路走到黑 说白了就是瞎几把递归 一般数据量较小的时候 可以采用这种方法 最基础的问题就是全排列的生成
#include <bits/stdc++.h>
using namespace std;
int a[25];
bool vis[25];
int n;
void dfs(int num)
{
if(num == n)
{
for(int i=0;i<n;i++)
cout << a[i] << ' ';
cout << '\n';
return ;
}
for(int i=1;i<=n;i++)
{
if(!vis[i])
{
vis[i] = 1;
a[num] = i;
dfs(num+1);
vis[i] = 0;
}
}
}
int main()
{
cin >> n;
dfs(0);
}
其中 dfs常见的格式为
if(!vis[i])
{
vis[i] = 1;
dfs(num+1);
vis[i] = 0;
}
也就是走到尽头回溯的过程中 将原来的状态还原
FJUT 2574
某个外国探险队在非洲沙漠中发现了二战时期希特勒埋藏的n个地下宝库(编号为1…n),并且还记下了从一个宝库走到另一个宝库的时间(有的宝库之间无通路,无通过时间)。几经知道每个宝库中藏有一定数量的宝藏,但由于德国法西斯在建造宝库时设有保护措施,因而每个宝库只能进入通过一次。在第一个宝库内可以取得打开其它宝库的钥匙(第一个宝库可以不用钥匙就进入)。另外,探险队还发现,在进入第一个宝库的同时将触发一个爆破装置,因而所有的宝库将在tk秒后同时爆炸。
请你设计一种最佳的从1开始的取宝藏的方案,使在宝库爆炸前(包括爆炸时)能取得最多的宝藏。
例:有4个宝库:1、2、3、4
其中宝藏数量为:100,60,80,70
通过时间(没有给出通过时间的表示无通路):
1—2 20秒
1—3 30秒
2—4 20秒
3—4 15秒
爆炸时间:50秒
此时方法有:
1—2—4
计40秒,可取100+60+70=230
或:
1—3—4
计45秒,可取100+80+70=250
则最佳方案为:
1—3—4
可取宝藏250
Input
包含多个测试数据,每个测试数据的第一行有三个用空格隔开的整数n(1≤n≤20), k(1≤k≤50), tk(1≤tk≤1000)。
其中n表示宝库的数目,k表示由k个通路,tk表示爆炸的时间。
接下来k行,每行有三个数据(x, y, t),表示从x→y或y→x所需时间t(即无向图)。
最后一行有n个整数,表示宝库中的宝藏数。
一行单独的一个0表示输入结束
Output
每行一个数,表示能取到的最多的宝藏数。
SampleInput
4 4 50
1 2 20
1 3 30
2 4 20
3 4 15
100 60 80 70
0
SampleOutput
250
#include <bits/stdc++.h>
using namespace std;
const int N = 25;
int mp[N][N];
bool vis[N];
int a[N];
int n,k,tk;
int res;
void dfs(int x,int time,int val)///x代表当前点 time为时间 val为宝藏数
{
res = max(res,val);
for(int i=1; i<=n; i++)
{
if(!vis[i] && (mp[x][i] || mp[i][x]))
{
if(time+mp[x][i] <= tk)
{
vis[i] = 1;
dfs(i,time+mp[x][i],val+a[i]);
vis[i] = 0;
}
}
}
}
int main()
{
while(~scanf("%d",&n) && n)
{
scanf("%d%d",&k,&tk);
res = 0;
memset(vis,0,sizeof(vis));
memset(mp,0x3f,sizeof(mp));///最为关键的初始化 没写直接T
while(k -- )
{
int x,y,t;
scanf("%d%d%d",&x,&y,&t);
mp[x][y] = mp[y][x] = t;
}
for(int i=1; i<=n; i++)
scanf("%d",&a[i]);
vis[1] = 1;
dfs(1,0,a[1]);
printf("%d\n",res);
}
return 0;
}
HDU 1181
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
string s,t;
bool vis[N];
bool flag;
int n;
void init()
{
s = t = "";
flag = false;
memset(vis,0,sizeof(vis));
}
void dfs(char temp)
{
int i;
if(temp == 'm')
{
flag = true;
return ;
}
for(int i=0;i<n;i++)
{
if(s[i] == temp && !vis[i])
{
vis[i] = 1;
/// temp = t[i]; dfs(temp); 如果改成这句则会出现错误
dfs(t[i]);
vis[i] = 0;
}
}
}
int main()
{
string str;
while(cin >> str)
{
init();
n = 0;
while(str[0] != '0')
{
s += str[0];
t += str[str.size()-1];
cin >> str;
n ++ ;
}
dfs('b');
if(flag)
cout << "Yes.";
else
cout << "No.";
cout << '\n';
}
return 0;
}
POJ 1562
dfs 求联通块个数
#include <iostream>
#include <cstring>
using namespace std;
const int N = 105;
char mp[N][N];
int n,m;
int res;
int dx[] = {1,-1,0,0,-1,-1,1,1};
int dy[] = {0,0,-1,1,1,-1,1,-1};
void dfs(int x,int y)
{
mp[x][y] = '*';
for(int i=0;i<8;i++)
{
int xx = x + dx[i];
int yy = y + dy[i];
if(xx >=0 && xx < n && yy >=0 && yy < m && mp[xx][yy] == '@')
dfs(xx,yy);
}
}
int main()
{
ios::sync_with_stdio(false);
while(cin >> n >> m && n && m)
{
int res = 0;
for(int i=0; i<n; i++)
cin >> mp[i];
/// key
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
if(mp[i][j] == '@')
{
res ++;
dfs(i,j);
}
}
}
cout << res << '\n';
}
return 0;
}
FJUT 2915
注意标记部分 不能只是单纯的标记为1 如果一个点被覆盖了两次 而你回溯一次的时候就把他标记成可使用状态 就会出错
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
int a[N][N];
int vis[N][N];
int res,n,m;
int dx[] = {0,1,-1,0,0,-1,-1,1,1};
int dy[] = {0,0,0,-1,1,1,-1,1,-1};
void dfs(int x,int y,int v)
{
if(y > m)
{
x ++ ;
y = 1 ;
}
if(x > n)
{
res = max(res,v);
return ;
}
if(!vis[x][y])
{
for(int i=0;i<9;i++)
vis[x+dx[i]][y+dy[i]] ++ ;
dfs(x,y+2,v+a[x][y]);
for(int i=0;i<9;i++)
vis[x+dx[i]][y+dy[i]] -- ;
}
dfs(x,y+1,v);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
scanf("%d",&a[i][j]);
res = 0;
dfs(1,1,0);
printf("%d\n",res);
}
return 0;
}
2.广度优先搜索
将可以从当前状态转换到下个状态全部情况存入队列中 再将当前转态抛弃 如此循环反复 直到找到正确答案 使用的空间会较大 且 一般得到的答案最短或者最优秀的
迷宫问题 记录路径的方法
#include <stdio.h>
#include <string.h>
#include <queue>
using namespace std;
char maps[10][10],vis[10][10],moves[4][2]={-1,0,1,0,0,1,0,-1};
struct node
{
int x,y;
};
node p,q,r,temp;
void bfs()
{
node pre[10][10],way[105];
queue<node>que;
que.push(p);
int top;
while(!que.empty())
{
r=que.front();
que.pop();
if(r.x==q.x&&r.y==q.y)
{
top=0;
while(1)
{
way[top++]=r;
if(r.x==p.x&&r.y==p.y)
break;
r=pre[r.x][r.y];
}
while(top--)
{
printf("(%d, %d)\n",way[top].x-1,way[top].y-1);
}
}
for(int i=0;i<4;i++)
{
temp.x=r.x+moves[i][0];
temp.y=r.y+moves[i][1];
if(!maps[temp.x][temp.y]&&!vis[temp.x][temp.y])
{
pre[temp.x][temp.y]=r;
vis[temp.x][temp.y]=1;
que.push(temp);
}
}
}
}
main()
{
p.x=p.y=1;
q.x=q.y=5;
memset(maps,1,sizeof(maps));
for(int i=1;i<=5;i++)
for(int j=1;j<=5;j++)
scanf("%d",&maps[i][j]);
bfs();
}
3.搜索优化
1.dfs剪枝 当确定当前状态已经无解时 直接返回 减少递归层数 缩短时间
HDU 1518
https://blog.csdn.net/weixin_44144278/article/details/102698238
2.bfs中如果已经出现过的状态 采用标记数组的方式 不将其如队列减少内存
HDU 1195
#include <bits/stdc++.h>
using namespace std;
string str;
struct node
{
string s;
int sum;
};
queue<node>que;
bool vis[10005];
bool check(string s)
{
int sum = (s[0]-'0')*1000 + (s[1]-'0')*100 + (s[2]-'0')*10 + s[3]-'0';
///如果已经出现的状态不会再入队列
if(vis[sum])
return 0;
vis[sum] = 1;
return 1;
}
void bfs()
{
node now;
while(!que.empty())
{
now = que.front();
que.pop();
if(now.s == str)
{
printf("%d\n",now.sum);
return ;
}
for(int i=0; i<4; i++)
{
node a,b;
string t = now.s;
if(t[i]!='9')
t[i] += 1;
else
t[i] = '1';
if(check(t))
{
a.s = t;
a.sum = now.sum +1;
que.push(a);
}
t = now.s;
if(t[i]!='1')
t[i] -= 1;
else
t[i] = '9';
if(check(t))
{
b.s = t;
b.sum = now.sum +1;
que.push(b);
}
if(i == 0)
{
t = now.s;
swap(t[i],t[i+1]);
if(check(t))
{
a.s = t;
a.sum = now.sum +1;
que.push(a);
}
}
else if(i > 0 && i < 3)
{
t = now.s;
swap(t[i],t[i+1]);
if(check(t))
{
a.s = t;
a.sum = now.sum +1;
que.push(a);
}
t = now.s;
swap(t[i],t[i-1]);
if(check(t))
{
a.s = t;
a.sum = now.sum +1;
que.push(a);
}
}
else if(i == 3)
{
t = now.s;
swap(t[i],t[i-1]);
if(check(t))
{
a.s = t;
a.sum = now.sum +1;
que.push(a);
}
}
}
}
}
int main()
{
int t;
ios::sync_with_stdio(false);
while(cin >> t)
{
while(t--)
{
memset(vis,0,sizeof(vis));
while(!que.empty())
que.pop();
string s;
cin >> s >> str;
node now;
now.s = s;
now.sum = 0;
que.push(now);
bfs();
}
}
return 0;
}
3.bfs打表 + cantor展开
#include <bits/stdc++.h>
using namespace std;
const int N = 15;
const int M = 100005;
char st[N],ed[N];
char change[N];
bool vis[M];
string res[M];
int fac[] = {1,1,2,6,24,120,720,5040,40320};
int mov[3][8] = {{7,6,5,4,3,2,1,0},{3,0,1,2,5,6,7,4},{0,6,1,3,4,2,5,7}};
struct node
{
char s[9];
};
int gethash(char *s)
{
int sum = 0;
int cnt ;
for(int i=0; i<8; i++)
{
cnt = 0;
for(int j=i+1; j<8; j++)
{
if(s[j] < s[i])
cnt ++ ;
}
sum += cnt*fac[8-i-1];
}
return sum;
}
void bfs()
{
queue<node>que;
node now;
for(int i=0; i<8; i++)
now.s[i] = i+'1';
int pos = gethash(now.s);
que.push(now);
vis[pos] = 1;
while(!que.empty())
{
now = que.front();
que.pop();
int posx = gethash(now.s);
node next;
for(int i=0; i<3; i++)
{
for(int j=0; j<8; j++)
next.s[j] = now.s[mov[i][j]];
int pos = gethash(next.s);
if(vis[pos])
continue;
vis[pos] = 1;
res[pos] = res[posx]+(char)('A'+i);
que.push(next);
}
}
}
int main()
{
bfs();
while(~scanf("%s%s",st,ed))
{
for(int i=0; i<8; i++)
change[st[i]-'1'] = i+'1';
for(int i=0; i<8; i++)
ed[i] = change[ed[i]-'1'];
int pos = gethash(ed);
cout << res[pos] << '\n';
}
return 0;
}