• 回溯法


    题目一:N皇后问题

      N皇后问题是典型的用回溯法解决的问题.其大概思想是:看某一层,如果这一层还有可以放的位置,就放下,否则回退到上一层;放下后看是否符合规则,若符合规则,则对下一层用同样的方式处理,若不符合规则,看这一行的下个,如此.

      所以总的看,可以递归实现.当然,这递归时可以转换成一般的循环的.

      其实从本质上看,可以看成树形结构,初始为根,是一个N*N的空二维表,然后有N个孩子节点,每个节点对应第一层的N中情况,然后这N个节点又有N个孩子节点以此类推,这就列出了所有可能的情况,回溯法就是沿着根到叶子的路径出发,一旦发现不合适的就回到父节点,看父节点的另一个孩子,以此类推,直到找到了从根到叶子的路径,此就是解.

      程序大概是这样的,这也是回溯法的一般代码模型:

    i=0;  //i可以看成上面所述的树的第i层,根为第0层
    While(结束条件,这里可以为i>=0)
    {
        If(第i层还有可以选择的情况)
        {
            If(第i层的这种情况符合规则) 
            { 
                选择这个;
                If(已经到了叶子节点)  说明找到了可行解,可以退出
                Else  到i+1层继续进行,一般是从i+1层第一个开始
             }
            Else
            {
                直接看i层的下一种情况
            }
        }
        Else    //第i层没有可以选择的情况
        {
            回退到i-1层,并标识i-1层的这种情况不行,意思就是下次看看i-1 层的下一种情况
        }
    }
    #include <iostream>
    
    #define N 25  //n皇后
    int table[N][N];
    
    void inite(void);
    void printResult(void);
    bool judgeIsOk(int posI,int posJ);
    
    using namespace std;
    
    int main(void)
    {
        int myI=0,myJ=0;   //表À¨ª示º?目?前¡ã处ä|理¤¨ª的Ì?位?置?
        int flag=0;   //标志,表示是有结果后跳出还是没有结果跳出
    
        while(myI>=0)
        {
            if(myJ<N)   //如¨?果?这a行D还1有®D可¨¦选?的Ì?
            {
                if(judgeIsOk(myI,myJ))  //并¡é且¨°还1是º?可¨¦用®?的Ì?
                {
                    table[myI][myJ]=1;  //选?用®?
    
                    if(myI==N-1)  //到Ì?了¢?最Á?后¨®一°?行D了¢?,ê?输º?出?结¨¢果?
                    {
                        printResult();
                        flag=1;
                        break;
                    }
                    else
                    {
                        myI++;  //进?入¨?下?一°?行D
                        myJ=0;  //记?得Ì?要°a把ã?这a初?始º?到Ì?0!ê?!ê?!ê?!ê?
                    }
                }
                else   //这a行D的Ì?这a个?不?可¨¦用®?,ê?则¨°看¡ä这a行D的Ì?下?一°?个?位?置?
                {
                    myJ++;
                }
            }
            else  //这a行D没?有®D可¨¦以°?选?择?的Ì?
            {
                myI--;  //则¨°回?跳¬?到Ì?上¦?一°?行D
    
                /*下?面?找¨°到Ì?上¦?一°?行D此ä?刻¨¬的Ì?位?置?*/
                int pos;
                for(pos=0;pos<N;pos++)
                {
                    if(table[myI][pos]==1)
                    {
                        break;
                    }
                }
    
                table[myI][pos]=0;  //并¡é且¨°要°a把ã?上¦?一°?行D的Ì?此ä?刻¨¬位?置?标À¨º为a不?可¨¦用®?
                myJ=pos+1;  //下?一°?次ä?循-环¡¤就¨ª到Ì?了¢?下?一°?个?位?置?
            }
        }
        if (!flag)
        {
            cout<<N<<"皇后没有解!"<<endl;
        }
        else
        {
            cout<<"上面为解的情况,1表示有皇后"<<endl;
        }
        cin.get();
    }
    
    /*初?始º?化¡¥*/
    void inite(void)
    {
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                table[i][j]=0;
            }
        }
    }
    
    /*给?定¡§一°?个?位?置?判D断?是º?不?是º?矛?盾¨¹*/
    bool judgeIsOk(int posI,int posJ)
    {
        /*先¨¨看¡ä列¢D*/
        for(int i=0;i<posI;i++)
        {
            if(table[i][posJ]==1)
            {
                return false;
            }
        }
    
        /*再¨´看¡ä右®¨°斜¡À线?*/
        int sum=posI+posJ;  //横¨¢竖º¨²坐Á?标À¨º的Ì?和¨ª
        for(int i=posI-1;i>=0;i--)
        {
            int j=sum-i;
            if(j<N)
            {
                if(table[i][j]==1)
                {
                    return false;
                }
            }
            else
            {
                break;
            }
    
        }
    
        /*看¡ä左Á¨®斜¡À线?*/
        int time=(posI>=posJ)?posJ:posI;  //教¨¬小?者?
        for(int i=1;i<=time;i++)
        {
            if(table[posI-i][posJ-i]==1)
            {
                return false;
            }
        }
        return true;   //最Á?终?返¤¦Ì回?true
    }
    
    /*输º?出?*/
    void printResult(void)
    {
        for(int i=0;i<N;i++)
        {
            for(int j=0;j<N;j++)
            {
                cout<<table[i][j]<<" ";
            }
            cout<<endl;
        }
    }
    View Code



    题目二:Game Show Math

      uva oj上的,解可以看做一棵树。我的做法超时了……

    #include <iostream>
    #include <stack>
    
    using namespace std;
    
    #define M_ERROR cout << "NO EXPRESSION
    "
    
    int main(void)
    {
        int cnt;
        cin >> cnt;
        
        while (cnt--)
        {
            //下面获得输入
            int p;
            cin >> p;
            int* arr = new int[p];
            for (int i = 0; i < p; i++)
                cin >> arr[i];
            int targetNum;
            cin >> targetNum;
    
            //下面判断
            if (p == 1)
            {
                if (arr[0] == targetNum)
                    cout << targetNum << "=" << targetNum << endl;
                else
                    M_ERROR;
            }
            else        //这里是回溯
            {
                struct Record        //定义一个记录上一层状态的的结构体,以便回溯法回溯时复原
                {
                    int curRes;    //第 i 个操作符(也就是第 i 层)左边的数值
                    int intOprator;            //第 i 层所采用的操作符代号:1、2、3、4
                };
                stack<Record> recordStack;
    
                int res = arr[0];        //运算结果
                int ii = 1;            //ii 代表层,也就是第几个运算符,[1, p-1]
                int jj = 1;        //jj代表列,也就是加减乘除,[1,4]
                bool flag = false;        //表明是成功跳出还是失败跳出
    
                while (ii)
                {
                    if (jj <= 4)        //如果第 ii 行还有可以选择的
                    {
                        Record recordTemp;
                        recordTemp.curRes = res;
                        recordTemp.intOprator = jj;
                        recordStack.push(recordTemp);
                        //下面选择这种情况
                        switch (jj)
                        {
                        case 1:
                            res += arr[ii];    
                            break;
                        case 2:
                            res -= arr[ii];
                            break;
                        case 3:
                            res *= arr[ii];
                            break;
                        case 4:        //除注意为零情况
                            res /= arr[ii];
                            break;
                        }
                        
                        if (ii == p - 1)        //如果到了叶子节点
                        {
                            if (res == targetNum)        //找到解
                            {
                                //获得全部的正序符号
                                int operatorCnt = recordStack.size();
                                int* arrOperator = new int[operatorCnt];
                                for (int k = operatorCnt - 1; k >= 0; k--)
                                {
                                    arrOperator[k] = recordStack.top().intOprator;
                                    recordStack.pop();
                                }
    
                                cout << arr[0];        //输出第一个
                                for (int k = 1; k < p; k++)
                                {
                                    int intOprator = arrOperator[k - 1];
                                    switch (intOprator)
                                    {
                                    case 1:
                                        cout << '+';
                                        break;
                                    case 2:
                                        cout << '-';
                                        break;
                                    case 3:
                                        cout << '*';
                                        break;
                                    case 4:
                                        cout << '/';
                                        break;
                                    }
                                    cout << arr[k];
                                }
                            
                                cout << "=" << targetNum << endl;
                                flag = true;
                                delete[] arrOperator;
                                break;
                            }
                            else    //到了叶子还没找到,看这一层的下一个,取消之前选择的
                            {
                                jj++;
                                res = recordStack.top().curRes;
                                recordStack.pop();
                            }
                        }
                        else   //这一层选择了,但是还没到叶子节点,那么去下一层
                        {
                            ii++;
                            jj = 1;
                        }
                    }
                    else        //如果第ii行没有可以选择的
                    {
                        //回溯
                        ii--;        //回溯到上一行
                        if (ii)        //注意最后
                        {
                            jj = recordStack.top().intOprator;
                            res = recordStack.top().curRes;
                            recordStack.pop();
                            jj++;        //回溯到上一行的下一个
                        }
                    }
                }
    
                if (!flag)
                    M_ERROR;
            }
            delete[] arr;
        }
    }
    View Code

       注意一下,可以用栈来记录每一层的数据,这样回溯时恢复到原先状态会更简单。

  • 相关阅读:
    Spring Boot
    Spring Boot Tomcat配置详解
    Spring Boot读取配置的 5 种方式
    Spring Boot日志集成实战
    Spring Boot国际化开发实战
    Spring Boot整合 Thymeleaf 模板引擎
    Spring Boot Debug调试
    Spring Boot实现热部署
    Exchange Cards(dfs)
    Dungeon Master
  • 原文地址:https://www.cnblogs.com/jiayith/p/3460217.html
Copyright © 2020-2023  润新知