• 回溯算法


    回溯算法理解

    深度优先搜索

    基本思想:将解的空间,转化为了树或者图的表示,进行深度优先搜索,并加以剪枝,在遍历过程中找到所有最优解或者可行解

    回溯算法的求解过程实质上是一个先序遍历一棵"状态树"的过程,只是这棵树不是遍历前预先建立的,而是隐含在遍历过程中

    当遍历某分支的时候,若发现条件不满足,则退回到根节点进入下一个分支的遍历。这就是“回溯”这个词的来源。而根据条件有选择的遍历,叫做剪枝或分枝定界

    伪代码

    总结一下,伪代码就是:
    
    void dfs(层数)
    {
        if(条件)
        {
            输出;
        }
        else
        {
            左子树的处理;
            dfs(层数+1);
            右子树的处理;
            dfs(层数+1);
        }
    }
    

    详细描述

    回溯法按深度优先策略搜索问题的解空间树。

    • 首先从根节点出发搜索解空间树,当算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能得到问题的解)。
    • 如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。
    • 回溯法的基本行为是搜索,搜索过程使用剪枝函数来为了避免无效的搜索。
    • 剪枝函数包括两类:
    1. 使用约束函数,剪去不满足约束条件的路径;
    2. 使用限界函数,剪去不能得到最优解的路径。

    框架

    bool finished = FALSE; /* 是否获得全部解? */
    backtrack(int a[], int k, data input)
    {
        int c[MAXCANDIDATES]; /*这次搜索的候选 */
        int ncandidates; /* 候选数目 */
        int i; /* counter */
        if (is_a_solution(a,k,input))
        process_solution(a,k,input);
        else {
            k = k+1;
            construct_candidates(a,k,input,c,&ncandidates);
            for (i=0; i<ncandidates; i++) {
                a[k] = c[i];
                make_move(a,k,input);
                backtrack(a,k,input);
                unmake_move(a,k,input);
                if (finished) return; /* 如果符合终止条件就提前退出 */
            }
        }
    }
    

    例一


    Description

    在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

    Input

    输入含有多组测试数据。
    每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目。 n <= 8 , k <= n
    当为-1 -1时表示输入结束。
    随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域, . 表示空白区域(数据保证不出现多余的空白行或者空白列)。

    Output

    对于每一组数据,给出一行输出,输出摆放的方案数目C (数据保证C<2^31)。

    Sample Input

    2 1

    .

    .#
    4 4
    ...#
    ..#.
    .#..

    ...

    -1 -1

    Sample Output

    2
    1

    代码如下:

    
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    
    using namespace std;
    int n, k;
    bool maze[9][9];
    int column[9] = {0};
    int ans = 0;
    
    //当前为第cur层,还剩下k个可以走
    void dfs(int cur,int k)
    {
        if(cur == n+1 || k == 0)
        {
            if( k == 0)
                ans++;
            return ;
        }
        //不选择cur层
        dfs(cur+1,k);
        //选择cur层
        for(int i = 1; i <= n ; i++)
        {
            if(maze[cur][i] == 1 && !column[i])
            {
                column[i] = 1;
                dfs(cur+1,k-1);
                //恢复现场
                column[i] = 0;
            }
        }
    }
    
    int main()
    {
        freopen("in.txt","r",stdin);
        while(cin >> n >> k)
        {
            memset(maze,0,sizeof(maze));
            memset(column,0,sizeof(column));
            if(n == -1 && k == -1)
                break;
            for(int i = 1 ; i <= n ; i++)
            {
                for(int j = 1 ; j <= n ; j++)
                {
                    char ch;
                    scanf("%c",&ch);
                    if(ch == '
    ')
                        scanf("%c",&ch);
                    if(ch == '#')
                        maze[i][j] = 1;
                }
            }
            ans = 0;
            dfs(1,k);
            cout << ans << endl;
        }
        return 0;
    }
    
    

    例二

    求一个集合的全部子集

    
    #include <iostream>
    #include <cstring>
    
    using namespace std;
    int n;
    int A[20],vis[20];
    
    void print_subset(int n, int *vis,int cur)
    {
        if(cur == n)
        {
            for(int i = 0 ; i < n ; i++)
            {
                if(vis[i])
                    cout << A[i] << "";
            }
            cout << endl;
            return ;
        }
        vis[cur] = 1;
        print_subset(n,vis,cur+1);
        vis[cur] = 0;
        print_subset(n,vis,cur+1);
    }
    
    int main()
    {
        int n;
        cin >> n;
        for(int i = 0; i < n ; i++)
            cin >> A[i];
        memset(vis,0,sizeof(vis));
        print_subset(n,vis,0);
        return 0;
    }
    
    

    输出不重复数字的全排列

    代码如下:
    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    
    using namespace std;
    int N;
    
    void permutaion(int *arr, int k,int N)
    {
        int i;
        if(N == k)
        {
            for(i = 0; i < N ; i++)
                cout << arr[i] << " ";
            cout << endl;
            return ;
        }
        for(i = k; i <N ; i++)
        {
            swap(arr[i],arr[k]);
            permutaion(arr,k+1,N);
            swap( arr[i],arr[k]);
        }
    }
    
    int main()
    {
        cin >> N;
        int arr[100];
        for(int i = 0 ; i < N ;i++)
            arr[i] = i+1;
        permutaion(arr,0,N);
        return 0;
    }
    
    
  • 相关阅读:
    cross apply / outer apply
    查看锁表
    常用的一些占位符
    如何将法向量转换到世界空间
    Essential Mathematics for Games and Interactive Applications
    下一本要看的书
    第五章第五节 四元数
    向量坐标转换、旋转矩阵以及视图转换
    怪诞行为学摘录
    unity3d发布apk资源打包和访问方式
  • 原文地址:https://www.cnblogs.com/pprp/p/7638043.html
Copyright © 2020-2023  润新知