zoukankan      html  css  js  c++  java
  • 搜索与图论总结

    1.深度优先搜素
    2.宽度优先搜索
    3.树与图的存储
    4.树与图的DFS
    5.树与图的BFS
    6.拓扑排序
    从使用数据结构来看
    DFS stack
    BFS queue
    从使用空间来看
    DFS O(H); “不具有最短性”
    BFS O(2^h) “最短路”(第一次搜到的距离为最短距离)

    1.深度优先搜索

    (1)回溯 : 注意恢复现场
    (2)剪枝 : 最优型剪枝 可行性剪枝
    一个DFS一定对应一个搜索树
    最重要的想清楚顺序

    例1.数字排列

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 20;
    int path[N], vis[N];
    int n;
    void dfs(int x)
    {
        if (x > n) {
            for (int i = 1; i <= n; i++)
                cout << path[i] << ' ';//输出路径
            cout << endl;
            return;
        }
        for (int i = 1; i <= n; i++)
        {
            if (!vis[i])
            {
                vis[i] = 1;//标记用过
                path[x] = i;//加入路径
                dfs(x + 1);//枚举下一个位置
                vis[i] = 0;//恢复现场
            }
        }
    }
    int main()
    {
        cin >> n;
        dfs(1);
        return 0;
    }
    

    例2:n皇后问题

    顺序1:枚举每一行的皇后放在什么位置
    
     / /
     / 正对角线上的元素坐标和相同 dg[x + y];
    
    \ 反对角线上的元素坐标差相同 由于y - x可能是负数所以加上n
    dg[y - x + n];
      
    #include<bits/stdc++.h>
    using namespace std;
    int n;
    const int N = 20;
    bool pal[N],pg[N],upg[N];
    char g[10][10];
    void dfs(int x) {
        if (x > n)
        {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++)
                    cout << g[i][j];
                cout << endl;
            }
            cout << endl;
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (!pal[i] && !pg[x + i] && !upg[n - i + x])
            {
                pal[i] = pg[x + i] = upg[n - i + x] = true;
                g[x][i] = 'Q';
                dfs(x + 1);
                pal[i] = pg[x + i] = upg[n - i + x] = false;
                g[x][i] = '.';
            }
        }
    
    }
    int main()
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = '.';
        dfs(1);
    
        system("PAUSE");
        return 0;
    }
    
    第二种
    这种方式是更无脑的搜索
    #include<bits/stdc++.h>
    using namespace std;
    int n;
    const int N = 20;
    char g[N][N];
    // 依次表示 行 列 正对角线 负对角线
    bool row[N], col[N], pg[N], upg[N];
    void dfs(int x, int y, int s) { //枚举行列和已经放皇后的个数
        if (y > n) {//注意! 这个是本题的重点
            x++;  //当y枚举到超过边界时 要让他指向下一行的第一个元素
            y = 1;
        }
        if (s >= n) {
            if (s == n) { //如果放皇后数目正好为n则输出方案
                for (int i = 1; i <= n; i++) {
                    for (int j = 1; j <= n; j++) {
                        cout << g[i][j];
                    }
                    cout << endl;
                }
                cout << endl;
            }
            return;
        }
        if (x > n) //如果x超过边界则return
            return;
    
        dfs(x, y + 1, s);//当前位置不放皇后
    
        //判断 行 列 正对角线 负对角线
        if (!row[x] && !col[y] && !pg[x + y] && !upg[n - x + y])
        {
            row[x] = col[y] = pg[x + y] = upg[n - x + y] = true;
            g[x][y] = 'Q'; //标记
            dfs(x, y + 1, s + 1);//这个位置放皇后 枚举下一个位置
            row[x] = col[y] = pg[x + y] = upg[n - x + y] = false;
            g[x][y] = '.';//恢复现场
        }
    }
    int main()
    {
        cin >> n;
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= n; j++)
                g[i][j] = '.';
        dfs(1, 1, 0);
        return 0;
    }
    

    2.BFS

    第一次搜到的一定是最小的(图中边的权重相同)

    只有边的权重都为1时求最短路才可以使用BFS
    DP是一种特殊的最短路 内部没有环的最短路
    基本步骤
    初始-> queue
    while queue 不空
    {
    t->队头
    拓展t
    }
    BFS在拓展时也要用vis 进行判断
    因为只有BFS第一次搜到的点才是最短距离

    例1

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 105;
    int a[N][N];
    int vis[N][N];
    int n, m;
    int ans = 0x3f3f3f3f;
    int dx[4] = { 0,0,1,-1 };
    int dy[4] = { 1,-1,0,0 };
    int k = 1;
    bool check(int x, int y)
    {
        return x >= 1 && x <= n && y >= 1 && y <= m;
    }
    struct node {
        int x;
        int y;
        int sum;
    }dia[100];
    void bfs(int x,int y,int sum) {
        queue<node> q;
        node dian;
        dian.x = x; dian.y = y; dian.sum = sum;
        q.push(dian);
        while (!q.empty())
        {
            int xx = q.front().x;
            int yy = q.front().y;
            int ss = q.front().sum;
            q.pop();
    
            if (xx == n && yy == m) {
                cout << ss << endl;
                return;
            }
            for (int i = 0; i < 4; i++) {
                int nowx = xx + dx[i];
                int nowy = yy + dy[i];
                if (check(nowx, nowy) && !vis[nowx][nowy] && a[nowx][nowy] == 0)
                {
                    node dian;
                    dian.x = nowx; dian.y = nowy; dian.sum = ss + 1;
                    vis[nowx][nowy] = 1;
                    q.push(dian);
                }
            }
        }
    
    }
    int main()
    {
        cin >> n >> m;
    
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                cin >> a[i][j];
            }
        }
        bfs(1,1,0);
        system("PAUSE");
        return 0;
    }
    

    例2

    #include<bits/stdc++.h>
    using namespace std;
    #include<unordered_map>
    
    int bfs(string start)
    {
    
        queue<string > q;
        vector<pair<string, int> > v;
        q.push(start);
        v
    
    }
    int main()
    {
        string start;
        cin >> start;
        cout << bfs(start) << endl;
        system("PAUSE");
        return 0;
    }
    
    #include<bits/stdc++.h>
    #include<unordered_map>
    using namespace std;
    int bfs(string state) {
    
        queue<string> q;
        unordered_map<string, int> d;
    
        q.push(state);
        d[state] = 0;
        int dx[4] = { -1,0,1,0 };
        int dy[4] = { 0,1,0,-1 };
        string end = "12345678x";
        while (q.size())
        {
            string t = q.front();
            q.pop();
    
            if (t == end)return d[t];
            int distance = d[t];
            int k = t.find('x');
            int x = k / 3, y = k % 3;
            for (int i = 0; i < 4; i++) {
                int a = x + dx[i], b = y + dy[i];
                if (a >= 0 && a < 3 && b >= 0 && b < 3) {
                    swap(t[a * 3 + b], t[k]);
                    if (!d.count(t))
                    {
                        d[t] = distance + 1;
                        q.push(t);
                    }
                    swap(t[a * 3 + b], t[k]);
                }
            }
        }
        return -1;
    }
    int main()
    {
        char s[2];
        string state;
        for (int i = 0; i < 9; i++)
        {
            cin >> s;
            state+= *s;
        }
        cout << bfs(state) << endl;
        system("PAUSE");
        return 0;
    }
    

    3.图论

    树和图是怎么存储的
    树和图有两种存储方式
    树是一种特殊的图,树是无环连通图
    图分为有向图和无向图

    一、只需要考虑有向图如何存储:

    1.邻接矩阵(用的比较少) 比较浪费空间
    二维数组 g[a][b] 表示a到b这一条边 g[a][b]的值就是a到b的权重

    2.邻接表
    为图中的每一个点都开一个单链表 存储这个点可以到达的点,单链表中的顺序无关紧要
    插入一条新的边从a到b,找到a所在的链表,从a的头把b插入进去
    模板:

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 100010, M = N * 2;//图是双向的,a可以到b,b也可以到a,n个点的图最多有2n条边
    int n, m;
    //h数组表示每个点的链表的头节点,e表示节点的值,ne表示节点的next指针,idx表示当前存储到的位置
    int h[N], e[M], ne[M], idx;
    void add(int a, int b) {//新建立一条边,将b这个节点插入到a链表头节点的后面
        e[idx] = b; 
        ne[idx] = h[a];
        h[a] = idx;
        idx++;
        //h[a]=idx++
    }
    int main()
    {
        memset(h, -1, sizeof(h));//将头节点都指向-1(都指向空节点)
        return 0;
    }
    
    

    二、树和图的遍历

    1.深度优先遍历
    2.宽度优先遍历
    都是每个点只遍历一次
    模板

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 100010, M = N * 2;//图是双向的,a可以到b,b也可以到a,n个点的图最多有2n条边
    int n, m;
    //h数组表示每个点的链表的头节点,e表示节点的值,ne表示节点的next指针,idx表示当前存储到第几条边
    int h[N], e[M], ne[M], idx;
    bool vis[N];
    void add(int a, int b) {//新建立一条边,将b这个节点插入到a链表头节点的后面
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx;
        idx++;
        //h[a]=idx++
    }
    //res 当前连通块的最大值
     
    void dfs(int u) {
        vis[u] = true;
        for (int i = h[u]; i != -1; i = ne[i]) {
            int j = e[i];
            if (!vis[j])dfs(j);//一条路走到黑
        }
    }
    
    int main()
    {
        memset(h, -1, sizeof(h));//将头节点都指向-1(都指向空节点)
        dfs(1);
        return 0;
    }
    

    最短路径 只遍历一次

    图的BFS的应用

    求拓扑序:
    1、概念
    拓扑序列 针对于有向图->对于每条边都是起点在终点的前面
    如果存在环 则一定不存在拓扑序
    一个有向无环图(拓扑图)一定存在拓扑序列
    入度:一个节点有几条边进来
    出度: 一个节点有几条边出去

    2、 拓扑序列的实现
    所有入度为0的点都可以作为一个起点

    所有入度为0的点入队

    while(q不空){
    t=队头
    枚举t的所有出边 t->j
    删掉t->j (因为t已经在j的前面了)
    d[j]-- d数组表示为j的入度
    if(d[j]==0)
    queue <-- j
    如果j的入度减为0了则j也可以作为一个起点入队
    }
    图的层次遍历·

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
    const int M = N * 2;
    int n, m;
    int h[N], e[M], ne[M], vis[N], idx;
    long long ans = 0x3f3f3f3f;
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx++;
    }
    void bfs(int num) {
        queue<pair<int,long long> > q;
        q.push(make_pair(num,0));
        vis[num] = 1;
        while (!q.empty())
        {
            pair<int,int> now = q.front();
            q.pop();
            for (int i = h[now.first]; i != -1; i = ne[i]) {
                int j = e[i];
                int foot = now.second + 1;
                if (j == n)
                {
                    ans = foot;
                    return;
                }
                if (vis[j])
                    continue;
                q.push(make_pair(j,foot));
    
            }
        }
    }
    int main()
    {
        cin >> n >> m;
        memset(h, -1, sizeof(h));
        for (int i = 1; i <= m; i++)
        {
            int a, b;
            cin >> a >> b;
            add(a, b);
        }
        bfs(1);
        if (ans = 0x3f3f3f3f)
            cout << -1 << endl;
        else cout << ans << endl;
        system("PAUSE");
        return 0;
    }
    

    有向图的拓扑排序

    /*图的bfs的应用
    求拓扑序:
    1、概念
    拓扑序列 针对于有向图->对于每条边都是起点在终点的前面
    如果存在环 则一定不存在拓扑序
    一个有向无环图(拓扑图)一定存在拓扑序列
    入度:一个节点有几条边进来
    出度 : 一个节点有几条边出去
    
        2、 拓扑序列的实现
        所有入度为0的点都可以作为一个起点
    
        所有入度为0的点入队
    
        while (q不空) {
            t = 队头
                枚举t的所有出边 t->j
                删掉t->j(因为t已经在j的前面了)
                d[j]--d数组表示为j的入度
                if (d[j] == 0)
                    queue < --j
                    如果j的入度减为0了则j也可以作为一个起点入队
        }
    一个有向无环图的拓扑序不是唯一的*/
    /*
    数组模拟队列实现BFS
    hh=0,tt=-1 hh代表队头 tt 代表队尾
    入队操作 q[++tt]=a;
    获取队头操作 t=q[hh++];
    判断队列是否为空 while(hh<=tt)
    出队操作q[++tt]=j;
    */
    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
    const int M = 2 * N;
    int n, m;
    int h[N], e[M], ne[M], idx, d[N], vis[N];
    int q[N];
    void add(int a, int b) {
        e[idx] = b;
        ne[idx] = h[a];
        h[a] = idx++;
    }
    int topsort()
    {
        int hh = 0, tt = -1;
        for (int i = 1; i <= n; i++)
        {
            if (d[i] == 0)
            {
                q[++tt] = i;
    
            }
        }
        while (hh <= tt) {
            int t = q[hh++];
            for (int i = h[t]; i != -1; i = ne[i]) {
                int j = e[i];
                if (--d[j] == 0)
                    q[++tt] = j;
            }
        }
        return tt == n - 1;
    
    }
    int main()
    {
        cin >> n >> m;
        memset(h, -1, sizeof(h));
        for (int i = 1; i <= m; i++)
        {
            int a, b;
            cin >> a >> b;
            add(a, b);
            d[b]++;
        }
        if (!topsort())
        {
            cout << -1 << endl;
        }
        else {
            for (int i = 0; i < n; i++)
                cout << q[i] << ' ';
            cout << endl;
        }
        return 0;
    }
    
    
    你以为的极限,也许只是别人的起点
  • 相关阅读:
    Angular2版本更新
    Angular2组件开发—调用服务(三)
    Angular2组件开发—调用服务(二)
    Angular2组件开发—调用服务(一)
    Angular2组件开发—表单输入(五)
    Angular2组件开发—表单输入(四)
    Angular2组件开发—表单输入(三)
    Angular2组件开发—表单输入(二)
    Angular2组件开发—表单输入(一)
    Angular2组件开发—属性与事件(二)
  • 原文地址:https://www.cnblogs.com/LengDing/p/14299443.html
Copyright © 2011-2022 走看看