日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【蓝桥杯C/C++】专题五:DFS深度优先搜索

發(fā)布時間:2024/3/13 c/c++ 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【蓝桥杯C/C++】专题五:DFS深度优先搜索 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

專題五:DFS深度優(yōu)先搜索

目錄

  • 專題五:DFS深度優(yōu)先搜索
  • 前言
  • 什么是回溯法
  • 如何理解回溯法
  • 回溯法解決的問題
  • 回溯法模板
    • 1 、回溯函數(shù)模板返回值以及參數(shù)
    • 2、 回溯函數(shù)終止條件
    • 3 、回溯搜索的遍歷過程
  • 回溯算法模板框架代碼如下
  • 遞歸實現(xiàn)指數(shù)型枚舉
    • 題目
    • 代碼及注釋
      • 方法一:遞歸枚舉法(子集)
      • 方法二:遞歸填坑法(每個數(shù)字選與不選)
      • 題解
  • 遞歸實現(xiàn)排列型枚舉
    • 題目
    • 代碼及注釋
    • 題解
  • 遞歸實現(xiàn)組合型枚舉
    • 題目
    • 代碼及注釋
    • 題解
  • 迷宮問題
    • 題目
    • 代碼及注釋
    • 題解
  • 01背包問題
    • 題目
    • 代碼及注釋
    • 題解
  • 八皇后
    • 題目
    • 代碼及注釋
    • 題解
  • 方格分割
    • 題目
    • 代碼及注釋
    • 題解
  • 組隊
    • 題目
    • 代碼及注釋
    • 題解
  • 總結(jié)

前言

本專題將講解算法競賽中最常用的算法dfs深度優(yōu)先搜索,也叫回溯搜索法或者“暴力搜索”法,也就是說在比賽的時候就算遇到?jīng)]有思路的題,也可以用遞歸實現(xiàn)暴力搜索來騙分。有的同學(xué)可能會過多的去糾結(jié)一些概念,比如遞歸、暴力搜索、回溯法、dfs等,其實我們大可不必去糾結(jié),因為dfs和回溯搜索法本身就是一個算法,是用遞歸操作來實現(xiàn)的,而“暴力搜索”則是民間賦予的稱號!! 以下內(nèi)容,我統(tǒng)稱為回溯法(我最喜歡的名字)!

什么是回溯法

回溯法也可以叫做回溯搜索法,它是一種搜索的方式。

回溯的本質(zhì)是窮舉,窮舉所有可能,然后選出我們想要的答案,也就是暴力搜索

如何理解回溯法

回溯法解決的問題都可以抽象為樹形結(jié)構(gòu),是的,我指的是所有回溯法的問題都可以抽象為樹形結(jié)構(gòu)!

因為回溯法解決的都是在集合中遞歸查找子集,集合的大小就構(gòu)成了樹的寬度,遞歸的深度,都構(gòu)成的樹的深度

遞歸就要有終止條件,所以必然是一棵高度有限的樹(N叉樹)。

遞歸里面嵌套著循環(huán),為單層搜索邏輯。

回溯法解決的問題

回溯法,一般可以解決如下幾種問題:

  • 組合問題:N個數(shù)里面按一定規(guī)則找出k個數(shù)的集合
  • 切割問題:一個字符串按一定規(guī)則有幾種切割方式
  • 子集問題:一個N個數(shù)的集合里有多少符合條件的子集
  • 排列問題:N個數(shù)按一定規(guī)則全排列,有幾種排列方式
  • 棋盤問題:N皇后,解數(shù)獨等等

回溯法模板

1 、回溯函數(shù)模板返回值以及參數(shù)

在回溯算法中,我的習(xí)慣是函數(shù)起名字為backtracking,這個起名大家隨意。

回溯算法中函數(shù)返回值一般為void。

再來看一下參數(shù),因為回溯算法需要的參數(shù)可不像二叉樹遞歸的時候那么容易一次性確定下來,所以一般是先寫邏輯,然后需要什么參數(shù),就填什么參數(shù)。

void backtracking(參數(shù))

2、 回溯函數(shù)終止條件

什么時候達到了終止條件,樹中就可以看出,一般來說搜到葉子節(jié)點了,也就找到了滿足條件的一條答案,把這個答案存放起來,并結(jié)束本層遞歸。

所以回溯函數(shù)終止條件偽代碼如下:

if (終止條件) {存放結(jié)果;return; }

3 、回溯搜索的遍歷過程

在上面我們提到了,回溯法一般是在集合中遞歸搜索,集合的大小構(gòu)成了樹的寬度,遞歸的深度構(gòu)成的樹的深度。

分享一張代碼隨想錄的圖

回溯函數(shù)遍歷過程偽代碼如下:

for循環(huán)就是遍歷集合區(qū)間,可以理解一個節(jié)點有多少個孩子,這個for循環(huán)就執(zhí)行多少次。

backtracking這里自己調(diào)用自己,實現(xiàn)遞歸。

大家可以從圖中看出for循環(huán)可以理解是橫向遍歷,backtracking(遞歸)就是縱向遍歷,這樣就把這棵樹全遍歷完了,一般來說,搜索葉子節(jié)點就是找的其中一個結(jié)果了。

回溯算法模板框架代碼如下

void backtracking(參數(shù)) {if (終止條件) {存放結(jié)果;return;}for (選擇:本層集合中元素(樹中節(jié)點孩子的數(shù)量就是集合的大小)) {處理節(jié)點;backtracking(路徑,選擇列表); // 遞歸回溯,撤銷處理結(jié)果} }

下面將會列舉一些例題以及真題。后續(xù)會繼續(xù)出一期專門的練習(xí)題!

遞歸實現(xiàn)指數(shù)型枚舉

題目


代碼及注釋

方法一:遞歸枚舉法(子集)

#include<iostream> #include<cstring> #include<algorithm> #include<vector> using namespace std; int n;vector<vector<int>> result;vector<int> path;void backtracking(int n, int startIndex) {result.push_back(path); // 收集子集,要放在終止添加的上面,否則會漏掉自己if (startIndex > n) { // 終止條件可以不加return;}for (int i = startIndex; i <=n; i++) {path.push_back(i);backtracking(n, i + 1);path.pop_back();}}int main(){cin>>n;backtracking(n,1);//記住二維向量的輸出方式!!for(int i=0 ; i <result.size(); i ++ )//把所有方案輸出 {for(int j=0;j<result[i].size();j++){printf("%d ",result[i][j]);}puts("");}}

📌本解法需要注意:子集必須再遞歸終止前收集 二維向量輸出的方法要會

方法二:遞歸填坑法(每個數(shù)字選與不選)

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N = 16;int n; int st[N]; // 狀態(tài),記錄每個位置當(dāng)前的狀態(tài):0表示還沒考慮,1表示選它,2表示不選它void dfs(int u) {if (u > n){for (int i = 1; i <= n; i ++ )if (st[i] == 1)printf("%d ", i);printf("\n");return;}st[u] = 2;dfs(u + 1); // 第一個分支:不選st[u] = 0; // 恢復(fù)現(xiàn)場st[u] = 1;dfs(u + 1); // 第二個分支:選st[u] = 0; }int main() {cin >> n;dfs(1);return 0; }

題解

遞歸填與不填,關(guān)鍵在于先畫出遞歸搜索樹,思路也就顯而易見了。


遞歸實現(xiàn)排列型枚舉

題目


代碼及注釋

#include <cstdio> #include <cstring> #include <iostream> #include <algorithm>using namespace std;const int N = 10;//題目中N的范圍是9,但我們?nèi)绻聵讼霃?開始,那么多用一個,開10 int n; //此題可以發(fā)現(xiàn)在搜索的時候還要保證每個數(shù)只搜索一次,要判斷當(dāng)前這個位置可以用的數(shù)有哪些, //因此還要存一個新的狀態(tài)used:表示每個數(shù)有沒有被用過 int state[N]; // 用st[]表示當(dāng)前的狀態(tài):0 表示還沒放數(shù),1~n表示放了哪個數(shù) bool used[N]; // true表示用過,false表示還未用過//注意變量如果定義成全局變量的話,初值會自動賦成0,如果定義成隨機變量的話,初值是一個隨機值 void dfs(int u) {if (u > n) // 邊界:枚舉完了最后一位,{for (int i = 1; i <= n; i ++ ) printf("%d ", state[i]); // 打印方案:只需要把當(dāng)前每個位置輸出出來 puts("");return;}// 依次枚舉每個分支,即當(dāng)前位置可以填哪些數(shù)for (int i = 1; i <= n; i ++ )//從小到大枚舉 if (!used[i])//如果當(dāng)前位置是沒有用過的,表示當(dāng)前位置可以填這個數(shù),成為一個分支,等價于used[i]==false{state[u] = i;//標記,更新狀態(tài) used[i] = true;//標記,更新狀態(tài),因為此題遞歸時需要兩個狀態(tài)表示 dfs(u + 1);//遞歸下一步 // 恢復(fù)現(xiàn)場,兩個狀態(tài)都要恢復(fù) state[u] = 0; //當(dāng)然,在這里state[]狀態(tài)數(shù)組其實可以不用恢復(fù),因為會直接覆蓋掉,但是為了更好的展現(xiàn)算法流程,方便初學(xué)者理解最好加上 used[i] = false;} }int main() {scanf("%d", &n);dfs(1);//從前往后枚舉,函數(shù)里面寫一個參數(shù),表示當(dāng)前枚舉到第幾位了 //因為state[]和used是全局變量了,所以不需要寫到函數(shù)參數(shù)里面 return 0; }

題解

對于全排列問題我們可以這樣想,第1個位置可以放1~n任意一個數(shù),第2個位置可以放除了放在第1個位置的數(shù)以外的任何一個數(shù),以此類推。因此我們可以畫出一個遞歸搜索樹,用map[]來表示儲存當(dāng)前排列。DFS函數(shù)要記住當(dāng)前處理的是第index個位置,從1到n進行遍歷,看看這個數(shù)是否可以放在第index個位置,需要有一個判重數(shù)組hashtable[x]來記錄x是否在排列里面。

遞歸實現(xiàn)組合型枚舉

題目


代碼及注釋

#include<iostream> #include<cstdio> using namespace std; const int N=30; int way[N]; int n,m; void dfs(int u,int start) {//剪枝if(u+n-start<m) return;//正在選第u個數(shù),已經(jīng)選了u-1個數(shù),還能選n-start+1個數(shù)if(u>m) {for(int i=1;i<=m;i++)printf("%d ",way[i]);printf("\n");return;}for(int i=start;i<=n;i++){way[u]=i;dfs(u+1,i+1);//way[u]=0;}} int main() {cin>>n>>m;dfs(1,1);return 0; }

題解

DFS的思路是這個樣子的,假設(shè)當(dāng)前處理的是第index個位置,這個位置可以放置start~n其中任意一個數(shù)。接著處理第index+1個位置,這個位置可以放置的最小數(shù)是前一位數(shù)的下一個數(shù)。即i+1~n

迷宮問題

題目

代碼及注釋

#include<bits/stdc++.h> using namespace std; const int N=100;int dx[4]={0,0,-1,1};//定義上下左右四個方向 int dy[4]={1,-1,0,0};int n,m,t; int sx,sy,fx,fy,l,r; int ans; bool visited[N][N]; int ditu[N][N]; bool check(int x,int y) {if(x<1||x>n||y<1||y>m) return false;//下標越界 if(ditu[x][y]) return false;//有障礙物 if(visited[x][y]) return false;//已經(jīng)訪問過該點 return true; }void dfs(int x,int y) {if(x==fx&&y==fy){ans++;return ;}for(int i=0;i<4;i++){int newx=x+dx[i];//走到下一個點 int newy=y+dy[i];if(check(newx,newy)){visited[x][y]=true;dfs(newx,newy);visited[x][y]=false;}} }int main() {cin>>n>>m>>t;cin>>sx>>sy>>fx>>fy;while(t--){cin>>l>>r;ditu[l][r]=1;}dfs(sx,sy);cout<<ans<<endl;return 0;}

題解

迷宮問題是拿來練習(xí)DFS與BFS很經(jīng)典的題目。迷宮問題有很多種問法,比如迷宮從起點到終點有沒有路徑,有幾條,最短路徑是多少。

求從起點到終點的方案數(shù)顯而易見也是要用DFS,遍歷所有的情況。我們要考慮這樣一個問題,迷宮里的某點(x,y)是否要被訪問呢。當(dāng)這點是障礙物肯定不能訪問,該點不在迷宮里面也不能訪問,該點訪問過了那就不能訪問了。(題目中有每個方格最多經(jīng)過一次)。因此我們需要一個check()函數(shù)來判斷某一點是否合法。合法我們就去訪問該點。

其實這個過程就是一個剪枝的過程,根據(jù)題目條件限制,剪掉一些不可能存在解的分支。

另外我們該如何知道某點是障礙點呢,可以設(shè)置一個map數(shù)組來表示該迷宮。

當(dāng)map[x][y]==1時表示該點是障礙點map[x][y]==0表示該點是正常點

01背包問題

題目

代碼及注釋

#include<bits/stdc++.h> using namespace std;const int N=1010; int n,m; int w[N], c[N]; int a[N][N]; int ans; //由于記錄了index不會出現(xiàn)重復(fù)遍歷的問題,不需要額外的標記數(shù)組 void dfs(int index,int sumv,int sumc) {if(index==n){if(sumv<=m){ans=max(ans,sumc);}return ;} //只有兩種情況不需要for循環(huán)了dfs(index+1,sumv+w[index],sumc+c[index]);//選第i個物品 dfs(index+1,sumv,sumc);//不選第i個物品 }int main() {cin>>n>>m;for (int i = 0; i < n; i++){cin >> w[i] >> c[i];}dfs(0,0,0);cout<<ans<<endl;return 0;}

題解

第i件物品無非就是選和不選兩種情況,在搜索的過程中DFS函數(shù)必須要記錄當(dāng)前處理的物品編號index,當(dāng)前背包的容量sumW,當(dāng)前的總價值sumC。

當(dāng)不選第index個物品時,那么sumW,sumC是不變的,接著處理第index+1個物品,也就是DFS(index+1, sumW, sumC)。

當(dāng)選擇第index個物品時,sumW變成sumW+w[index],sumC變成sumC+v[index],接著處理第index+1個物品,也就是DFS(index+1, sumW+w[index],sumC+v[index])。邊界條件也就是把最后一件物品也處理完了,即index=n(注意默認index從0開始)。

當(dāng)一條分支結(jié)束了該干什么呢,很簡單呀就是判斷該分支最終滿不滿足總重量不大于背包容量。即sumW<=v。滿足的話我們就更新價值maxvalue,即maxvalue=max(maxvalue,sumC)

八皇后

題目

代碼及注釋

#include<bits/stdc++.h>using namespace std; int n; const int N=100; int a[100],b[100],c[100],d[100]; int ans;bool check(int i,int j) {if(!b[j]&&!c[j-i+n]&&!d[i+j]) return true;//注意這里的對角線表達式只能為這個 return false; }void dfs(int i) {if(i>n){ans++;if(ans<=3){for(int i=1;i<=n;i++){cout<<a[i]<<" ";}cout<<endl;}return ;}for(int j=1;j<=n;j++)//枚舉一行中所有列的棋子 {if(check(i,j)){a[i]=j;b[j]=1;c[j-i+n]=1;d[i+j]=1;dfs(i+1);b[j]=0;c[j-i+n]=0;d[i+j]=0;}} }int main() {cin>>n;dfs(1);//從第一行開始枚舉 cout<<ans<<endl;return 0;}

題解

這道題DFS的思路還是比較清晰的,每行有且只有一個棋子,那么DFS可以記錄下當(dāng)前處理的是第幾行的棋子。假設(shè)當(dāng)前處理的是第i行的棋子,那么要枚舉處在該行的棋子位置,判斷哪個是合法的。
什么樣的位置算是合法的呢,這個位置的列還有左對角線,右對角線位置都不能有棋子。那么又該如何表示這些位置呢?我們采用一維數(shù)組來分別表示列,左對角線,右對角線。列很好表示就是b[j],左對角線我們可以發(fā)現(xiàn)行減去列的絕對值是恒定的,即c[i-j+n],右對角線行加列是恒定的。即d[i+j]。

方格分割

題目

代碼及注釋

#include<bits/stdc++.h> using namespace std;int maze[7][7];//表示已經(jīng)訪問過的點,1表示已經(jīng)訪問的 int dx[4]={0,1,0,-1}; int dy[4]={-1,0,1,0};int ans;void dfs(int x,int y) {if(x==0||y==0||x==6||y==6){ans++;return ;}for(int i=0;i<4;i++){int a=x+dx[i],b=y+dy[i];if(maze[a][b]!=1){maze[a][b]=1;maze[6-a][6-b]=1;dfs(a,b);maze[a][b]=0;//回溯maze[6-a][6-b]=0;}} }int main() {maze[3][3]=1;//中心點 標記已經(jīng)訪問dfs(3,3);cout<<ans/4<<endl;//旋轉(zhuǎn)對稱只算一種方式return 0; }

題解

本題需要發(fā)現(xiàn)一個規(guī)律:分割成的兩部分一定是中心對稱的,也就是從中心點開始上下左右搜索的結(jié)果就是答案,但是需要記錄已經(jīng)搜索過的點,以及旋轉(zhuǎn)對稱只算一種方式。

畫個圖會清晰很多,寫出點的坐標,并算出中心對稱的坐標。

組隊

題目

代碼及注釋

#include<bits/stdc++.h> using namespace std;int maze[20][20]; bool visited[20]; int ans; void dfs(int index,int sum) {if(index==5)//枚舉當(dāng)前是第幾位,固定列{ans=max(ans,sum);return ;}for(int i=0;i<20;i++)//枚舉每一行{if(maze[i][index]!=0&&!visited[i]){visited[i]=true;dfs(index+1,sum+maze[i][index]);visited[i]=false;a}} }int main() {for(int i=0;i<20;i++)for(int j=0;j<5;j++)cin>>maze[i][j];dfs(0,0); }

題解

本題是經(jīng)典的dfs模型,可以看成是排列問題,對于一號位來說有20種選擇,對于二號位來說有19種選擇,也就是說可以維護當(dāng)前正在選擇第index位,每一次dfs更新一個最大值。

縱向遞歸:index代表第幾位

橫向枚舉:一共有20個選手,for循環(huán)枚舉。

參數(shù):index sum

回溯數(shù)組:visited[N] 表示已經(jīng)訪問過的選手

總結(jié)

本文主要講解了回溯搜素算法的原理、模板以及具體的代碼與例題。需要大家熟練掌握算法,多做練習(xí)題,才能在比賽中靈活運用該算法。預(yù)祝各位考出好成績!!

總結(jié)

以上是生活随笔為你收集整理的【蓝桥杯C/C++】专题五:DFS深度优先搜索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 天天做天天爱天天做 | 亚洲精品乱码久久久久久 | 亚洲视频中文 | 五月天伊人网 | www在线播放 | 一级全黄毛片 | 羞羞影院体验区 | 97精品熟女少妇一区二区三区 | 顶级黄色片 | 青草视频在线看 | 亚洲欧洲天堂 | 变态另类一区二区 | 成人依人| 久久99热这里只有精品 | 全部免费毛片在线播放一个 | 成人免费av在线 | 免费色播 | 免费暧暧视频 | 91精品综合久久 | 日日摸天天爽天天爽视频 | xxxxhdvideos | 青青草十七色 | 女~淫辱の触手3d动漫 | 夜夜cao| 亚洲国产成人精品激情在线 | 少妇人妻一区二区 | 毛片大片 | 国产xxxx做受性欧美88 | 成人在线免费观看视频 | 麻豆精品国产 | 男女一级特黄 | 伊人久艹| 黄色a大片| 欧美日韩在线视频免费观看 | 高潮流白浆在线观看 | 91日韩中文字幕 | 偷拍久久久 | 国产精品无码免费在线观看 | 激情久久综合 | 97色伦图片| 免费看美女被靠到爽的视频 | 欧美日韩1区2区3区 亚洲日本精品视频 | 国产老女人精品毛片久久 | 欧美色99| 欧美日韩一区二区三区69堂 | 一区视频在线免费观看 | 久久精品欧美视频 | 最污网站在线观看 | 香蕉av网站| 亚洲欧美日韩精品久久亚洲区 | 亚洲AV无码精品色 | 一本视频 | 黄色网免费 | 国产一区视频网站 | 青青视频免费 | 日本少妇与黑人 | 97香蕉| 香蕉视频97 | 亚洲喷水 | 蜜桃精品在线 | 波多野结衣一区二区三区 | 不卡av在线| 日本成人免费网站 | 米奇狠狠干| 黄色一级片欧美 | 国产精品国产三级国产a | 成人自拍网站 | 天堂av官网 | 亚洲综合久久av一区二区三区 | 老局长的粗大高h | 国产99久久九九精品无码免费 | 麻豆精品视频在线 | 一级黄色网 | 亚洲AV无码成人精品区东京热 | 五月依人网 | 久久免费视频观看 | 日韩在线| 亚州av网 | 男人天堂网址 | 亚洲无码高清精品 | 国产超碰人人模人人爽人人添 | 91在线高清视频 | 欧美私人情侣网站 | 日韩欧美一区二区视频 | 亚洲视频免费在线 | 日韩成人av一区 | 天天夜碰日日摸日日澡性色av | 超碰免费在线播放 | 成人国产a| 久久午夜精品视频 | 久热在线| 成年视频在线 | 日本精品一区二区三区在线观看 | 成人黄色在线播放 | 国产成人一区二区三区电影 | 国产中文字幕一区二区三区 | 国产东北女人做受av | 一区二区三区 日韩 | 国产精品视频久久 |