题目一: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; } }
题目二: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; } }
注意一下,可以用栈来记录每一层的数据,这样回溯时恢复到原先状态会更简单。