DFS(深度優(yōu)先搜索算法)

深度優(yōu)先搜索算法(Depth First Search钠糊,簡稱DFS)

是一種用于遍歷或搜索樹或圖的算法砍艾。 沿著樹的深度遍歷樹的節(jié)點悯衬,盡可能深的搜索樹的分支码耐。
當節(jié)點v的所在邊都己被探尋過或者在搜尋時結點不滿足條件追迟,搜索將回溯到發(fā)現(xiàn)節(jié)點v的那條邊的起始節(jié)點。
整個進程反復進行直到所有節(jié)點都被訪問為止骚腥。
屬于盲目搜索,最糟糕的情況算法時間復雜度為O(!n)敦间。

深度優(yōu)先搜索算法難以尋找最優(yōu)解,僅僅只能尋找有解束铭。其優(yōu)點就是內(nèi)存消耗小

演示

求圖中的V0出發(fā)廓块,是否存在一條路徑長度為4的搜索路徑


深度優(yōu)先搜索.png

有這樣一個解的:V0->V3->V5->V6。

處理過程:


處理過程.png

基本模板

int check(參數(shù))
{
    if(滿足條件)
        return 1;
    return 0;
}
 
void dfs(int step)
{
        判斷邊界
        {
            相應操作
        }
        嘗試每一種可能
        {
               滿足check條件
               標記
               繼續(xù)下一步dfs(step+1)
               恢復初始狀態(tài)(回溯的時候要用到)
        }
}  

實例

1契沫、全排列問題
//全排列問題
#include<stdio.h>
#include<string.h>
 
int n;
char  a[15];
char re[15];
int vis[15];
//假設有n個字符要排列带猴,把他們依次放到n個箱子中
//先要檢查箱子是否為空,手中還有什么字符懈万,把他們放進并標記拴清。
//放完一次要恢復初始狀態(tài),當?shù)絥+1個箱子時钞速,一次排列已經(jīng)結束
void dfs(int step)
{
    int i;
    if(step==n+1)//判斷邊界
    {
        for(i=1;i<=n;i++)
            printf("%c",re[i]);
        printf("\n");
        return ;
    }
    for(i=1;i<=n;i++)//遍歷每一種情況
    {
        if(vis[i]==0)//check滿足
        {
            re[step]=a[i];
            vis[i]=1;//標記
            dfs(step+1);//繼續(xù)搜索
            vis[i]=0;//恢復初始狀態(tài)
        }
    }
    return ;
}
 
int main(void)
{
    int T;
    scanf("%d",&T);
    getchar();
    while(T--)
    {
        memset(a,0,sizeof(a));
        memset(vis,0,sizeof(vis));//對存數(shù)據(jù)的數(shù)組分別初始化
        scanf("%s",a+1);
        n=strlen(a+1);
        dfs(1);//從第一個箱子開始
    }
    return 0;
}

2贷掖、一個環(huán)由個圈組成,把自然數(shù)1渴语,2苹威,…,N分別放在每一個圓內(nèi)驾凶,數(shù)字的在兩個相鄰圈之和應該是一個素數(shù)牙甫。 注意:第一圈數(shù)應始終為1。

input: N(0~20)
output:輸出格式如下所示的樣品调违。每一行表示在環(huán)中的一系列圓號碼從1開始順時針和按逆時針方向窟哺。編號的順序必須滿足上述要求。打印解決方案的字典順序技肩。

//Prime Ring Problem
//與上面的全排列問題其實思路差不多且轨,只是需要判斷的條件比較多
//化大為小
 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
int book[25];
int result[25];
int n;
int num;
//判斷是否為素數(shù)
int prime(int n)
{
    if(n<=1)
        return 0;
    int i;
    for(i=2;i*i<=n;i++)
    {
        if(n%i==0)
            break;
    }
    if(i*i>n)
        return 1;
    return 0;
}
//判斷是否能將當前的數(shù)字放到當前的圈內(nèi)
int check(int i,int step)
{
    if((book[i]==0) && prime(i+result[step-1])==1)
    {
        if(step==n-1)
        {
            if(!prime(i+result[0]))
                return 0;
        }
        return 1;
    }
    return 0;
}
 
void dfs(int step)
{
    if(step==n)//判斷邊界
    {
        int a;
        printf("%d",result[0]);
        for(a=1;a<n;a++)
        {
            printf(" %d",result[a]);
        }
        printf("\n");
        return ;
    }
    int i;
    for(i=2;i<=n;i++)//遍歷每一種情況
    {
        if(check(i,step))//check是否滿足
        {
            book[i]=1;//標記
            result[step]=i;//記錄結果
            dfs(step+1);//繼續(xù)往下搜索
            book[i]=0;//恢復初始狀態(tài)
        }
    }
}
 
int main(void)
{
 
    while(scanf("%d",&n)!=EOF)
    {
        num++;
        memset(result,0,sizeof(result));
        memset(book,0,sizeof(book));
        result[0]=1;
        printf("Case %d:\n",num);//格式比較容易出錯
        dfs(1);
        printf("\n");
    }
    return 0;
}

3、油田問題

問題:GeoSurvComp地質調查公司負責探測地下石油儲藏。 GeoSurvComp現(xiàn)在在一塊矩形區(qū)域探測石油旋奢,并把這個大區(qū)域分成了很多小塊泳挥。他們通過專業(yè)設備,來分析每個小塊中是否蘊藏石油至朗。如果這些蘊藏石油的小方格相鄰屉符,那么他們被認為是同一油藏的一部分。在這塊矩形區(qū)域锹引,可能有很多油藏矗钟。你的任務是確定有多少不同的油藏。

input: 輸入可能有多個矩形區(qū)域(即可能有多組測試)嫌变。每個矩形區(qū)域的起始行包含m和n吨艇,表示行和列的數(shù)量,

1<=n,m<=100初澎,如果m =0表示輸入的結束秸应,接下來是n行,每行m個字符碑宴。每個字符對應一個小方格软啼,并且要么是’*’,代表沒有油延柠,要么是’@’祸挪,表示有油。

output: 對于每一個矩形區(qū)域贞间,輸出油藏的數(shù)量贿条。兩個小方格是相鄰的,當且僅當他們水平或者垂直或者對角線相鄰(即8個方向)增热。

//A - Oil Deposits 
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
char a[105][105];
int n,m,result;
int dir[8][2]={{1,0},{-1,0},{0,1},{0,-1},{1,1},{-1,-1},{1,-1},{-1,1}};//表示8個方向
 
int check(int x,int y)//檢查是否有油田
{
    if(x>=0&&x<m&&y>=0&&y<n&&a[x][y]=='@')
        return 1;
    return 0;
}
 
int dfs(int x, int y)
{
    int i,xx,yy;
    if(check(x,y))
    {
        a[x][y]='.'; //統(tǒng)計之后就可以把該油田標記整以,且不用恢復(要不會重復),
                    //也可以用一個數(shù)組來存每個點的訪問情況峻仇,但是感覺沒必要公黑,浪費空間
        for(i=0;i<8;i++)
        {
            xx=x+dir[i][0];
            yy=y+dir[i][1];
            dfs(xx,yy);//依次檢查8個方向
        }
        return 1;
    }
    return 0;
}
 
int main(void)
{
    int i,j;
    while(scanf("%d %d",&m,&n)==2)
    {
        if(m==0&&n==0)
            break;
        result = 0;
        memset(a,0,sizeof(a));
        for(i=0;i<m;i++)
            scanf("%s",a[i]);
        for(i=0;i<m;i++)//在每一個點都搜索一次
        {
            for(j=0;j<n;j++)
            {
                if(dfs(i,j))//找到油田就可以將結果加1
                    result++;
            }
        }
        printf("%d\n",result);
    }
    return 0;
}

4、棋盤問題

問題:在一個給定形狀的棋盤(形狀可能是不規(guī)則的)上面擺放棋子摄咆,棋子沒有區(qū)別凡蚜。要求擺放時任意的兩個棋子不能放在棋盤中的同一行或者同一列,請編程求解對于給定形狀和大小的棋盤吭从,擺放k個棋子的所有可行的擺放方案C朝蜘。

input: 輸入含有多組測試數(shù)據(jù)。 每組數(shù)據(jù)的第一行是兩個正整數(shù)涩金,n k谱醇,用一個空格隔開暇仲,表示了將在一個n*n的矩陣內(nèi)描述棋盤,以及擺放棋子的數(shù)目枣抱。 n <= 8 , k <= n 當為-1 -1時表示輸入結束熔吗。 隨后的n行描述了棋盤的形狀:每行有n個字符,其中 # 表示棋盤區(qū)域佳晶, . 表示空白區(qū)域(數(shù)據(jù)保證不出現(xiàn)多余的空白行或者空白列)。

output:對于每一組數(shù)據(jù)讼载,給出一行輸出轿秧,輸出擺放的方案數(shù)目C (數(shù)據(jù)保證C<2^31)。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
 
int n, k, ans;
char str[10][10];
int vis[100];
 
void dfs(int r, int k)
{
    if(k==0)//判斷邊界咨堤,此時棋子已經(jīng)放完
    {
        ans++;
        return;
    }
 
    for(int i=r; i<n; i++)//每次都從放過棋子下一行開始搜索菇篡,保證不重復
    {
        for(int j=0; j<n; j++)
        {
            //循環(huán)保證行不重復,check保證列不重復
            if(str[i][j]=='.' || vis[j]==1)
                continue;//不滿足條件直接跳過
            vis[j] = 1;//標記
            dfs(i+1, k-1);//繼續(xù)下一次標記
            vis[j] = 0;//恢復初始狀態(tài)
        }
    }
}
 
int main(void)
{
    while(1)
    {
        scanf("%d %d", &n, &k);
        getchar();
        if(n==-1 && k==-1) 
            break;
        memset(str, '\0', sizeof(str));
        memset(vis, 0, sizeof(vis));
        ans = 0;
 
        for(int i=0; i<n; i++)
        {
            for(int j=0; j<n; j++)
                str[i][j] = getchar();
            getchar();
        }
 
        dfs(0, k);//從第0行開始放一喘,此時手中還剩k個棋子
        printf("%d\n", ans);
    }
    return 0;
}

關于連通分量的個數(shù)

在無向圖中驱还,如果從頂點vi到頂點vj有路徑,則稱vi和vj連通凸克。

如果圖中任意兩個頂點之間都連通议蟆,則稱該圖為連通圖
否則萎战,稱該圖為非連通圖咐容,
則其中的極大連通子圖稱為連通分量
這里所謂的極大是指子圖中包含的頂點個數(shù)極大蚂维。

例如:一個無向圖有5個頂點戳粒,1-3-5是連通的,2是連通的虫啥,4是連通的蔚约,則這個無向圖有3個連通分量。

連通分量的個數(shù)就是DFS使用的次數(shù)

參考資料:
https://blog.csdn.net/ldx19980108/article/details/76324307#commentBox
https://blog.csdn.net/qq_40763929/article/details/81629800
https://www.cnblogs.com/DWVictor/p/10048554.html

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末涂籽,一起剝皮案震驚了整個濱河市苹祟,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌又活,老刑警劉巖苔咪,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異柳骄,居然都是意外死亡团赏,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門耐薯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來舔清,“玉大人丝里,你說我怎么就攤上這事√遐耍” “怎么了杯聚?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長抒痒。 經(jīng)常有香客問我幌绍,道長,這世上最難降的妖魔是什么故响? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任傀广,我火速辦了婚禮,結果婚禮上彩届,老公的妹妹穿的比我還像新娘伪冰。我一直安慰自己,他們只是感情好樟蠕,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布贮聂。 她就那樣靜靜地躺著,像睡著了一般寨辩。 火紅的嫁衣襯著肌膚如雪吓懈。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天捣染,我揣著相機與錄音骄瓣,去河邊找鬼。 笑死耍攘,一個胖子當著我的面吹牛榕栏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蕾各,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼扒磁,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了式曲?” 一聲冷哼從身側響起妨托,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎吝羞,沒想到半個月后兰伤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡钧排,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年敦腔,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恨溜。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡符衔,死狀恐怖找前,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情判族,我是刑警寧澤躺盛,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站形帮,受9級特大地震影響槽惫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜沃缘,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一躯枢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧槐臀,春花似錦、人聲如沸氓仲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽敬扛。三九已至晰洒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間啥箭,已是汗流浹背谍珊。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留急侥,地道東北人砌滞。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像坏怪,于是被迫代替她去往敵國和親贝润。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345