回溯法

题目一: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\n"

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

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

转载于:https://www.cnblogs.com/jiayith/p/3460217.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值