• [原]回溯法(组合问题、八皇后问题)


    【Title】[原]回溯法(组合问题、八皇后问题)
    【Date】2013-03-29
    【Abstract】回溯法示例包括组合问题和八皇后问题。很久之前在书上抄的,复习一下,分享出来。自己一个字一个字的打上来的(有部分精简),标上原创,版权属于原作者。
    【Keywords】算法、algorithm、backtracking method
    【Environment】Windows 7
    【Author】wintys (wintys@gmail.com) http://wintys.cnblogs.com
    【URL】http://www.cnblogs.com/wintys/archive/2013/03/29/algorithm_backtracking.html

    【Content】:

    1、回溯法要点


        回溯法:首先放弃关于规模大小的限制。
        a.试探(扩大规模):满足除规模之外的所有条件,则扩大规模。
        b.回溯(缩小规模):当前规模解不是合法解时回溯(不满足约束条件);求完一个解,再求下一个解时,也要回溯。
     

    1.1、组合问题


        问题描述:找出从自然数1,2,...,n中任取r个数的所有组合。
        【分析】:
        采用回溯法找问题的解,将找到的组合以从小到大的顺序存于a[0],a[1],...,a[r-1]中,组合的元素满足以下性质:
        (1)、a[i+1] > a[i],后一个数据字比前一个数字大。
        (2)、a[i] - i <= n - r + 1。
     
        【伪代码】
        void combination(int n, int r)
        {
            int i,j;
            //搜索深度、广度初始化
            i = 0;
            a[i]=1;
     
            do{
                if(a[i] - i <= n - r +1)//深度、广度的限制条件,性质(2)
                {
                    if(i == r - 1)//规模达到要求
                    {
                        print a[0..r-1];//输出合格的一组解
                        a[i]++;//当前规模的下一个解,即广度加大
                        continue;
                    }
                    i++;//扩大问题规模,即深度加大
                    a[i] = a[i - 1] +1;//为满足性质(1)
                }
                else//当前规模考查完,回溯。
                {
                    if( i == 0)//回溯至开始,已找到问题全部解,搜索结束。
                        return;
                    a[--i]++;//缩小规模,考查下一个候选解,深度减小,广度加大。
                }
            }while(1);
        }

    1.2、八皇后问题


        问题描述:求出在一个nxn的棋盘上,放置n个不能互相捕捉的国际象棋“皇后”的所有布局。
        【分析】
        a.直观的方法是采用一个二维数组,但仔细观察就会发现,这种表示方法给调整候选解及检查其合理性带来困难。更好的方法是尽可能直接表示那些常用的信息。对于本题而言,常用信息并不是皇后的具体位置,而是“一个皇后”是否已经在某行某斜线合理地安置好。一列上只能放一个皇后,因此引入col[]数组。
        b.为了使程序在找完全部解后回溯到最初位置,设定col[0]的初值为0,当回溯到第0列时,表示已求得全部解,程序结束。
        c.每行、每列、每条斜线上只能有一个皇后。引入数组为了便于检查皇后是否安置好。值为1表是没有皇后,为0表示有皇后。
        数组a[k]表示第k行上没有皇后。
        数组b[k]表示第k列右高左低斜线(/)上没有皇后。
        数组c[k]表示第k列左高右低斜线(\)上没有皇后。
        d.棋盘中同一右高左低斜线(/)上的方格,它们的行号与列号之和相同;同一左高右低斜线(\)上的方格,它们的行号与列号之差相同。
        e.初始时,所有行和斜线上均没有皇后,从第1列的第一行配置第一个皇后开始,在第m列col[m]行放置了一个合理的皇后后,准备考查第m+1列时,在数组a[]、b[]、c[]中为第m列,col[m]行的位置设定有皇后标志;当从第m列回溯到第m-1列,并准备调整第m-1列的皇后配置时,清除在数组a[]、b[]和c[]中设置的关于第m-1列,col[m-1]行有皇后的标志。一个皇后在m列,col[m]行方格内配置是合理的,由数组a[]、b[]和c[]对应位置的值都为1来确定。由于每次只在某列配置一个皇后,故不事能有两个皇后在同一列的情况。
        f.棋盘的列数m称为问题的规模。
        g.此程序的关键是试探条件:a[col[m]]&&b[m + col[m]]&&c[n+m-col[m]];
     
     
        【代码】
        #include <stdio.h>
        #include <stdlib.h>
        #define MAX_N 20
     
        int n,m,good;
        int col[MAX_N + 1],a[MAX_N + 1],b[2*MAX_N + 1],c[2*MAX_N + 1];
     
        void main()
        {
            int j;
            char awn;
            printf("Enter n:");
            scanf("%d",&n);
     
            for(j=0;j<=n;j++)a[j]=1;//初始时,每列都能放皇后
            for(j=0;j<=2*n;j++)b[j]=c[j]=1;//初始时,每斜线都能放皇后。
            m = 1; col[1] = 1; good = 1; col[0] = 0;
     
            do{
                if(good)
                {
                    if(m == n)//找到一个解
                    {
                        printf("列\t行");
                        for(j=1;j<=n;j++)
                            printf("%3d\t%3d\n",j,col[j]);//输出皇后位置
                        scanf("%c",&awn);
                        if(awn== 'Q'|| awn=='q')exit(0);
                        while(col[m] == n)//问题当前规模m是否还有其它解
                        {
                            m--;//当前规模m无其它解则回溯
                            a[col[m]]=b[m+col[m]]=c[n+m-col[m]] = 1;//回溯后清除皇后标志
                        }
                        col[m]++;//试探当前规模的下一解
                    }
                    else//在m列、col[m]行,设置皇后标志
                    {
                        a[col[m]]=b[m+col[m]]=c[n+m-col[m]] = 0;
                        col[++m]=1;//扩大规模,试探该规模下的第一个可能解,即皇后从第一行开始配置
                    }
                }
                else//good为0时的情况,即当前规模下试探解失败,需回溯或调整皇后位置
                {
                    while(col[m]==n)//是否当前规模下所有可能解试探都失败
                    {
                        m--;//当前规模下所有可能解试探都失败,需回溯
                        a[col[m]]=b[m+col[m]]=c[n+m-col[m]] = 1;//回溯后,清除皇后标志。
                    }
                    col[m]++;//当前规模下可能还有其它解,调整当前规模下的皇后位置。
                }
                //当前规模m下放置的皇后是否合法
                good = a[col[m]]&&b[m + col[m]]&&c[n+m-col[m]];
            }while(m != 0);
         }
    【Reference】

    [1]《软件设计师考试考点分析与真题详解(最新版)》

  • 相关阅读:
    20130118
    延迟加载、分页显示等功能的增加
    ==和Equals的区别
    20160115--Hibernate
    20160108--搜索查询
    20150105
    20151229--数据表格
    20151226--easyUI
    DreamWeaver使用技巧(转)
    20121109
  • 原文地址:https://www.cnblogs.com/wintys/p/algorithm_backtracking.html
Copyright © 2020-2023  润新知