一.搜索算法的概念及其種類:
1.概念:
利用計算機的高效性,窮舉出問題的部分或者所有的解,是求解問題的一種方法。本質上是通過初始條件擴展出問題的“解答樹”,去尋找問題的解。構建這樣的樹形模型主要由兩個部分——控制結構(擴展節點的方式)和產生系統(擴展節點)。通過對每個節點不同方式的拓展去尋找目標解。
2.種類:
- 深度優先搜索(DFS)
- 廣度優先搜索(BFS)
- 二分搜索
- 散列法(Hash)
二.算法實例:
1.深度優先搜索 :
深度優先搜索可用棧和遞歸實現,深度優先搜索和回溯法類似都屬於窮舉搜索,只是回溯大多可以寫在dfs中,只是回溯法需要返回上一結點的狀態,判斷是否使用深度優先搜索需要考慮的因素有:
- 是否可以通過列出所有答案求解。
- 是否可以找到限制條件進行剪枝。
基礎模板
void dfs()//參數用來表示狀態
{
if(到達終點狀態)
{
...//根據題意添加
return;
}
if(越界或者是不合法狀態)
return;
if(特殊狀態)//剪枝
return ;
for(擴展方式)
{
if(擴展方式所達到狀態合法)
{
修改操作;//根據題意來添加
標記;
dfs();
(還原標記);
//是否還原標記根據題意
//如果加上(還原標記)就是 回溯法
}
}
}
例題
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,C[15],vis[3][30],tot;//三個標記分別記錄列,主對角線,副對角線
void init()
{
memset(C,0,sizeof(C));
memset(vis,0,sizeof(vis));
tot=0;
}
void dfs(int cur)
{
if(cur==n) {
if(tot<3)
{
for(int i=0;i<n;i++)
printf("%d ",C[i]+1);
printf("\n");
}
tot++;
}
else
for(int i=0;i<n;i++) {
if(!vis[0][i]&&!vis[1][i+cur]&&!vis[2][cur-i+n])//每條主對角線行+列相等,每條副對角線行-列相等
{
C[cur]=i;
vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=1;
dfs(cur+1);
vis[0][i]=vis[1][i+cur]=vis[2][cur-i+n]=0;//一定要還原,回溯的基本操作
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF){
init();
dfs(0);
printf("%d",tot);
}
return 0;
}
- 素數環
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
using namespace std;
int n,book[20],ans[20];
int isprime[40]={0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0};
void init(){
memset(book,0,sizeof(book));
memset(ans,0,sizeof(ans));
}
void dfs(int step){
if(step==n)
{
if(isprime[ans[n-1]+ans[0]])
{
for(int i=0;i<n;i++)
if(i!=n-1)
printf("%d ",ans[i]);
else
printf("%d",ans[i]);
printf("\n");
}
return;
}
for(int i=2;i<=n;i++)
{
if(!book[i]&&isprime[i+ans[step-1]])
{
ans[step]=i;
book[i]=1;
dfs(step+1);
book[i]=0;
}
}
}
int main()
{
int t=1;
while(scanf("%d",&n)!=EOF){
init();
printf("Case %d:\n",t++);
if(n==1){
printf("%d\n\n",1);
continue;
}
ans[0]=1;
book[1]=1;
dfs(1);
printf("\n");
}
return 0;
}
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,T,sx,sy,ex,ey,ans=0;
int mp[6][6],turn[4][2]={0,1,1,0,0,-1,-1,0};
void dfs(int x,int y)
{
if(x==ex&&y==ey)
ans++;
else
{
int nx,ny;
for(int i=0;i<4;i++)
{
nx=x+turn[i][0];
ny=y+turn[i][1];
if(nx>=1&&nx<=n&&ny>=1&&ny<=m&&mp[nx][ny])
{
mp[nx][ny]=0;
dfs(nx,ny);
mp[nx][ny]=1;
}
}
}
}
int main()
{
memset(mp,1,sizeof(mp));
scanf("%d%d%d%d%d%d%d",&n,&m,&T,&sx,&sy,&ex,&ey);
for(int i=1;i<=T;i++)
{
int temp1,temp2;
scanf("%d%d",&temp1,&temp2);
mp[temp1][temp2]=0;
}
mp[sx][sy]=0;
dfs(sx,sy);
printf("%d",ans);
return 0;
}
- HDU Robot Motion(給定方向搜索類似模擬)
在初始化book數組時,如果初始化爲0,則對於一進入地圖就是loop的數據就會出現錯誤,因爲第一次經過入口時給入口的值爲0,以至於檢測是否循環時發生錯誤。以後對標記數組初始化時,將其初始化爲-1。
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,sy,book[15][15],answer1,answer2,flag=0;
char mp[15][15];
void dfs(int x,int y,int step)
{
if(x<0||x>n-1||y<0||y>m-1)
{
answer1=step;
return;
}
if(book[x][y]!=-1)
{
answer1=book[x][y];
answer2=step-book[x][y];
flag=1;
return;
}
book[x][y]=step;
if(mp[x][y]=='N')
dfs(x-1,y,step+1);
else if(mp[x][y]=='S')
dfs(x+1,y,step+1);
else if(mp[x][y]=='W')
dfs(x,y-1,step+1);
else if(mp[x][y]=='E')
dfs(x,y+1,step+1);
}
int main()
{
while(scanf("%d%d%d",&n,&m,&sy),n!=0||m!=0||sy!=0)
{
memset(book,-1,sizeof(book));
memset(mp,'0',sizeof(mp));
for(int i=0;i<n;i++)
scanf("%s",mp[i]);
dfs(0,sy-1,0);
if(flag)
printf("%d step(s) before a loop of %d step(s)\n",answer1,answer2);
else
printf("%d step(s) to exit\n",answer1);
answer1=answer2=flag=0;
}
return 0;
}
2.廣度優先搜素:
屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。它不同於深度優先的是,它是通過一個節點去拓展離它最近且滿足條件的節點,逐層遍歷,因此可以找到從初始狀態到最終狀態的路徑。
基礎模板:
void bfs(起始點) {
將起始點放入隊列中;
標記起點訪問;
while (如果隊列不爲空) {
訪問隊列中隊首元素x;
刪除隊首元素;
for (x 所有相鄰點) {
if (該點未被訪問過且合法) {
將該點加入隊列末尾;
}
}
}
隊列爲空,廣搜結束;
}