DFS深度優先算法學習

剛開始學習算法,參考大佬博客仍是有不少不明白的,因而一步步解析,寫下筆記記錄。ios

大佬博客地址:算法

https://blog.csdn.net/fuzekun/article/details/85220468數組

問題描述
  n我的參加某項特殊考試。
  爲了公平,要求任何兩個認識的人不能分在同一個考場。
  求是少須要分幾個考場才能知足條件。
輸入格式
  第一行,一個整數n(1<n<100),表示參加考試的人數。
  第二行,一個整數m,表示接下來有m行數據
  如下m行每行的格式爲:兩個整數a,b,用空格分開 (1<=a,b<=n) 表示第a我的與第b我的認識。
輸出格式
  一行一個整數,表示最少分幾個考場。
樣例輸入
5
8
1 2
1 3
1 4
2 3
2 4
2 5
3 4
4 5
樣例輸出
4
樣例輸入
5
10
1 2
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
樣例輸出
5
由於數據量很小可使用回溯算法。
應用兩層回溯:
第一層回溯是將考生放在不一樣考場裏面產生的效果,好比學生3號能夠放在教室1和教室2中那麼放在那一個教室會產生更好的效果這是一層回溯。
第二層回溯是考生放入之前的考場仍是考生本身從新用一個考場。好比考生3號能夠放進教室1和教室2,也能夠放進教室3。
應用簡單的剪枝技巧:
如今考場的數量已經大於之前最少的考場數量了就不用在展開了。
由於題目中沒有時間限制,因此能夠不使用vector,使用二維數組存放邊,使用二維數組存放教室中學生。
另外使用vecot中的find()老是報錯因此就不使用了
使用二維數組的時間複雜度約爲O(nn)由於沒一個學生須要遍歷以前的考場就須要遍歷每一個人一次1 + 2…+n - 1;
使用vector最好的狀況是NlogN,這種狀況對應只有一個考場,每一個學生耗費logN的複雜度,NlogN,最壞的狀況就會退成O(NN),對應着N個考場,每一個以前的學生都須要遍歷一次。

#include<stdio.h>
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
const int maxn = 100 + 5;
int room[maxn][maxn];//保存教室i中第j個學生的id
int gra[maxn][maxn];//存圖
int m,n;
int res = maxn + 500;//保存答案
void solve(int id,int num)
{
    //printf("%d %d\n",id,num);
    if(num >= res){//剪枝
        return ;
    }
    if(id > n){//學生的編號從1開始防止room[][] = 0不是沒學生的標誌
        res = min(res,num);
        return ;
    }
    for(int i = 1;i <= num;i++){//找到id是否有符合的教室
        int k = 0,flag = 1;
        while(room[i][k]){//查找學生教室中是否有認識的人
           // printf("%d %d %d\n",id,room[i][k],gra[id][room[i][k]]);
            if(gra[id][room[i][k]]){//教室裏面有認識的人
                //printf("衝突\n");
                flag = 0;
                break;
            }
            k++;//不要寫在數組裏面防止執行順序的不對而出錯
        }
        if(flag){                //回溯必定對應着判斷
            room[i][k] = id;
            solve(id+1,num);
            room[i][k] = 0;        //第一層回溯
        }
    }
//從新開啓一個教室
    room[num+1][0] = id;
    solve(id+1,num+1);
    room[num+1][0] = 0;//第二層回溯
}
void init()
{
    memset(room,0,sizeof(room));
}
int main()
{
    init();
    scanf("%d%d",&n,&m);
    for(int i = 0;i < m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        gra[a][b] = gra[b][a] = 1;
    }
    solve(1,0);
    printf("%d\n",res);
    return 0;
}
函數

分析:
假設5我的,1,2,3,4,5    1,3,5互相認識
程序開始
輸入 n=5 5我的  m=?命令數
初始 room[i][j] =0   i爲房間,j爲學生
輸入
1 3
1 5
3 5
gra[1][3] = gra[3][1] =1
gra[1][5] = gra[5][1] =1
gra[3][5] = gra[5][3] =1
solve(id=1,num=0){  id爲學生,num爲房間數
    room[1][0] =1
    solve(id=2,num=1){
        room[1][1]=2
        solve(id=3,num=1){
            room[2][0]=3
            solve(id=4,num=2){
                room[1][2]=4
                solve(id=5,num=2){
                    room[3][0]=5
                    solve(id=6,num=3){
                        res=num=3
                        return
                    }
                    room[3][0]=0
                }
                room[1][2]=0
                room[2][1]=4
                solve(id=5,num=2){
                    room[3][0]=5
                    solve(id=6,num=3){
                        res=num=3
                        return
                    }
                    room[3][0]=0
                }
                room[2][1]=0
                room[3][0]=4
                solve(id=5,num=3){
                    3 >= 3 剪枝
                    return
                }
                room[3][0]=0
            }
            room[2][0]=0
        }
        room[1][1]=0
        room[2][0]=2
        solve(id=3,num=2){
            room[2][1]=3
            solve(id=4,num=2){
                room[1][1]=4
                solve(id=5,num=2){
                    room[3][0]=5
                    solve(id=6,num=3){
                        res =3
                        return
                    }
                    room[3][0]=0
                }
                room[1][1]=0
                room[2][2]=4
                solve(id=5,num=2){
                    room[3][0]=5
                    solve(id=6,num=3){
                        res =3
                        return
                    }
                    room[3][0]=0
                }
                room[3][0]=4
                solve(id=5,num=3){
                    剪枝
                    return
                }
            }
            room[2][1]=0
            room[3][0]=3
            solve(id=4,num=3){
                    剪枝
                    return
                }
        }
        room[2][0]=0
    }
    room[2][0]=2
    solve(id=3,num=2)
    ……
}

遞歸邏輯步驟
首先進入函數,
1判斷當前的num屋子數是否大於等於已記錄的屋子數res,若是符合則剪枝return
2判斷當前的人id是否超過總人數,若是符合則記錄當前屋子數res且return        
3循環判斷如今的人id在已存在的num屋子數是否有認識人,若是沒有則放在相應的後面
4建立一個新屋子,將如今的人放入,並將下一我的進行判斷

假設過程:
id=1的人沒有房子,開設新房子num=1並放入,
    遞歸id=2,發現num=1能夠放入,
        遞歸id=3,發現num=1有認識的人,開設新房間num=2放入
            遞歸id=4,發現num=1能夠放入
                遞歸id=5,發現num=1,num=2有認識的人,開設新房間num=3放入
                遞歸id=6,發現超過人數,記錄下第一次獲得的房間數res=num=3
            回溯id=4,不放進num=1,放入num=2
                遞歸id=5,發現num=1,num=2有認識的人,開設新房間num=3放入
                遞歸id=6,發現房間數num=3與res相等,表示不會出現更優解,進行剪枝
            回溯id=4,不放進num=1,num=2,開設新房間num=3
                遞歸id=5,發現房間數num=3與res相等,表示不會出現更優解,進行剪枝
            id=4中止
        id=3中止
    回溯id=2,開設新房間num=2放入
        遞歸id=3,發現num=1有認識的人,放入num=2中
            遞歸id=4,發現num=1能夠放入
                遞歸id=5,發現num=1,num=2有認識的人,開設新房間num=3放入
                遞歸id=6,發現超過人數,記錄下第一次獲得的房間數res=num=3
            回溯id=4,不放進num=1,放入num=2
                遞歸id=5,發現num=1,num=2有認識的人,開設新房間num=3放入
                遞歸id=6,發現房間數num=3與res相等,表示不會出現更優解,進行剪枝
            回溯id=4,不放進num=1,num=2,開設新房間num=3
                遞歸id=5,發現房間數num=3與res相等,表示不會出現更優解,進行剪枝
            id=4中止
        遞歸id=3,開設新房間num=3,發現房間數num=3與res相等,表示不會出現更優解,進行剪枝
        id=3中止
    id=2中止
id=1中止

細節:首先進行剪枝判斷來優化程序,當id數超過總人數表示一條支路到盡頭,到盡頭後往前回溯上一級的另一種可能性,一樣到盡頭後再回溯,直到頂級結束
全部的回溯以開設新房間爲最差方式結束。
思想核心:首先選擇一個起點,先沿着一條路走到盡頭,而後回溯上一步走另一種可能,全部可能性走完後繼續回溯上級再走上級的另外可能
學習

整體的步驟圖(差很少能看。。):優化

spa

.net

blog

 

 ④遞歸

 

 ⑤

 

 ⑥

 

 ⑦

 

 (回到起點結束)

相關文章
相關標籤/搜索