算法分析与设计实验三 回溯法 24点问题 n皇后问题
实验目的
学习编程实现深度优先搜索状态空间树求解实际问题的方法, 着重体会求解第一个可行解和求解所有可行解之间的差别。 加深理解回溯法通过搜索状态空间树、同时用约束函数剪去不含答案状态子树的算法思想, 会用蒙特卡罗方法估计算法实际生成的状态空间树的结点
数。
实验内容
1、 求 24 点问题
给定四个 1-9 之间的自然数, 其中每个数字只能使用一次,用算术运算符+,-,*,/构造出一个表达式,将这 4 个正整数连接起来(可以使用括号),使最终的得数为 24。 要求根据问题的特征设计具体算法并编程实现, 输入数据为 4 个自然数。输出若有多个满足要求的表达式, 则只输出其中一组; 若搜索失败, 则输出“ Fail!”。
【示例】 采用一个表达式中用括号确定运算先后次序的方式,如:
输入 1, 5, 5, 5 四个自然数,输出((5-(1/5))*5)。
输入 3, 3, 8, 8 四个自然数,输出(8/(3-(8/3)))。
【测试数据】
(1) 1,5,5,5 (2) 3,3,8,8 (3) 3,8,8,8 (4) 1,2,3,4 (5) 2,4,5,6
(6) 4,2,2,5 (7) 1,2,2,6 (8) 4,2,8,8 (9) 0,3,8,8
2、 n 皇后问题
要求用回溯法求解 8-皇后问题, 使放置在 8*8 棋盘上的 8 个皇后彼此不受攻击, 即:任何两个皇后都不在同一行、 同一列或同一斜线上。 请输出 8 皇后问题的所有可行解。
学习编程实现深度优先搜索状态空间树求解实际问题的方法, 着重体会求解第一个可行解和求解所有可行解之间的差别。 加深理解回溯法通过搜索状态空间树、同时用约束函数剪去不含答案状态子树的算法思想, 会用蒙特卡罗方法估计算法实际生成的状态空间树的结点
数。
实验内容
1、 求 24 点问题
给定四个 1-9 之间的自然数, 其中每个数字只能使用一次,用算术运算符+,-,*,/构造出一个表达式,将这 4 个正整数连接起来(可以使用括号),使最终的得数为 24。 要求根据问题的特征设计具体算法并编程实现, 输入数据为 4 个自然数。输出若有多个满足要求的表达式, 则只输出其中一组; 若搜索失败, 则输出“ Fail!”。
【示例】 采用一个表达式中用括号确定运算先后次序的方式,如:
输入 1, 5, 5, 5 四个自然数,输出((5-(1/5))*5)。
输入 3, 3, 8, 8 四个自然数,输出(8/(3-(8/3)))。
【测试数据】
(1) 1,5,5,5 (2) 3,3,8,8 (3) 3,8,8,8 (4) 1,2,3,4 (5) 2,4,5,6
(6) 4,2,2,5 (7) 1,2,2,6 (8) 4,2,8,8 (9) 0,3,8,8
2、 n 皇后问题
要求用回溯法求解 8-皇后问题, 使放置在 8*8 棋盘上的 8 个皇后彼此不受攻击, 即:任何两个皇后都不在同一行、 同一列或同一斜线上。 请输出 8 皇后问题的所有可行解。
24点程序:
/* Function : 求解24点问题,回溯,dfs。 * Name : 24点.cpp * Author : wyh7280 * Time : 2015.05.20 17:15:00.000 * Update : 2015.05.25 12:10:00.000 */ #include <iostream> #include <string> #include <cmath> #include <cstdlib> #include <cstdio> #include <ctype.h> using namespace std; //宏定义 const double PRECISION = 1E-6; //精度常量 const int COUNT_OF_NUMBER = 4; //算24点的自然数个数 const int NUMBER_TO_BE_CAL = 24; bool flag=false; int cnt=0; //标记解的数量,为1时退出 class RationalNumber //定义有理数类(分子、分母) { protected: int numerator,denominator; //numerator:分子,denominator:分母 bool inf; protected: int gcd(int a,int b) //求最大公约数,辗转相除法 { int temp; if(a<b) { temp=a; a=b; b=temp; } while(temp=a%b) { a=b; b=temp; } return b; } public: RationalNumber() //constructor { inf=false; } RationalNumber(int n) { numerator=n; denominator=1; inf=false; } RationalNumber(int numerator,int denominator) { this->numerator=numerator; this->denominator=denominator; Simplify(); } virtual ~RationalNumber() {} void Simplify() { if(denominator==1) { inf=false; } else if(numerator==0) { denominator=1; inf=false; } else { int k=gcd(abs(numerator),abs(denominator)); numerator/=k; denominator/=k; if(denominator==1) inf=false; else inf=true; } } //重载运算符 RationalNumber operator+(const RationalNumber& b) const { RationalNumber result; result.denominator=this->denominator*b.denominator; result.numerator=this->numerator*b.denominator+this->denominator*b.numerator; result.Simplify(); return result; } RationalNumber operator-(const RationalNumber& b) const { RationalNumber result; result.denominator=this->denominator*b.denominator; result.numerator=this->numerator*b.denominator-this->denominator*b.numerator; result.Simplify(); return result; } RationalNumber operator*(const RationalNumber& b) const { RationalNumber result; result.denominator=this->denominator*b.denominator; result.numerator=this->numerator*b.numerator; result.Simplify(); return result; } RationalNumber operator/(const RationalNumber& b) const { RationalNumber result; result.denominator=this->denominator*b.numerator; result.numerator=this->numerator*b.denominator; result.Simplify(); return result; } RationalNumber& operator=(const RationalNumber& b) { denominator=b.denominator; numerator=b.numerator; return (*this); } RationalNumber& operator=(int b) { denominator=1; numerator=b; return (*this); } int Numerator() const { return numerator; } int Denominator() const { return denominator; } string strshow() { string str; char buffer[20]; itoa(numerator,buffer,10); str=buffer; if(denominator!=1) { itoa(denominator,buffer,10); str=str+"/"+buffer; } return str; } }; RationalNumber number[COUNT_OF_NUMBER]; //用数组number[]保存操作数 string expression[COUNT_OF_NUMBER]; //用数组expression[]保存算式 bool Search(int n) //递归函数负责寻找可行解,其中 n 为本层调用的操作数个数。 每次两 //数运算后,原来的两个操作数被去除,运算结果成为新的操作数,因此总的操作数数量减 1。 { if (n==1) { if (fabs(number[0].Numerator ()*1.0/number[0].Denominator ()-NUMBER_TO_BE_CAL)<PRECISION&&cnt==0) { cout<<"Output one avaiable solution: "<<expression[0]<<endl; //输出表达式 flag=true; cnt=1; return true; } else { // flag=false; return false; } } int i,j,k; RationalNumber temp; for ( i=0;i<n;i++) { if(n==4){ temp=number[i]-number[i+1]; if(temp.Numerator ()==0) continue; } for (j=i+1;j<n;j++) { if(n==4) { for(k=i+1;k<j;k++) { temp=number[j]-number[k]; if(temp.Numerator ()==0) break; } if(k<j) continue; } RationalNumber a, b; string expa, expb; a=number[i]; //用a保存number[i] b=number[j]; //用b保存number[j] number[j]=number[n-1]; //将number[n-1]向前填入到原来number[j]的位置 expa=expression[i]; //用expa保存expression[i] expb=expression[j]; //用expb保存expression[j] expression[j]=expression[n-1]; //将expression[n-1]向前填入到原来expression[j]的位置 //因为下一层递归调用search(n-1)将仅对下标为0~n-2的数进行操作了 //加法 expression[i]='('+expa+'+'+expb+')'; //将a和b计算的算式填入到原来expression[i]的位置 number[i]=a+b; //将a和b计算的结果填入到原来number[i]的位置 // Search(n-1); if (Search(n-1)&&n!=4) // { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true;} //一旦得到一个可行解,即层层向上返回,从而确保只输出一个可行解 //减法有两种情况a-b和b-a expression[i]='('+expa+'-'+expb+')'; number[i]=a-b; if(number[i].Numerator ()>=0) { // Search(n-1); if(Search(n-1)&&n!=4) { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true; } } expression[i]='('+expb+'-'+expa+')'; number[i]=b-a; if(number[i].Numerator() >=0) { // Search(n-1); if(Search(n-1)&&n!=4) { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true; } } //乘法 expression[i]='('+expa+'*'+expb+')'; number[i]=a*b; // Search(n-1); if(Search(n-1)&&n!=4) { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true; } //除法也有两种情况a/b和b/a if(b.Numerator()!=0) { expression[i]='('+expa+'/'+expb+')'; number[i]=a/b; // Search(n-1); if(Search(n-1)&&n!=4) { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true; } } if(a.Numerator()!=0) { expression[i]='('+expb+'/'+expa+')'; number[i]=b/a; // Search(n-1); if(Search(n-1)&&n!=4) { number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; return true; } } //本轮调用完毕后,用a,b,expa,expb将数组number[]和expression[]恢复原状 number[i]=a; number[j]=b; expression[i]=expa; expression[j]=expb; } } return false; } int main() { int x; cout<<"Please input four integers:"; flag=false; for(int i=0;i<COUNT_OF_NUMBER;i++) { char buffer[20]; //分配长度为20的字符数组buffer[] cin>>x; number[i]=x; itoa(x,buffer,10); //itoa():将一个10进制的integer数转换为string类型 //即:把输入的int型操作数x,转变成可以放在buffer[]中的string类型 expression[i]=buffer; //用expression[i]指针指向buffer[]数组空间的起始位置 } Search(4); if(flag==false) cout<<"Fail\n"; return 0; }
n皇后程序(主函数调用实现8皇后求解)
/* Function : 求解 n-皇后问题,本题求解8皇后(可自行修改n值),用回溯法深度优先遍历状态空间树, 并利用约束函数进行剪枝。 * Name : 8皇后问题.cpp * Author : wyh7280 * Time : 2015.05.26 12:01:00.000 * Update : no */ #include <iostream> #include <cstdio> #include <cstdlib> #include <math.h> using namespace std; bool Place(int k,int i,int *x); void NQueens(int k,int n,int *x);; void NQueens(int n,int *x); int main() { int x[8]; for(int i=0;i<8;i++) x[i]=-1; NQueens(8,x); } bool Place(int k,int i,int *x) //判定两个皇后是否在同一列或在同一斜线上 { for (int j=0;j<k;j++) if ((x[j]==i)||(abs(x[j] -i)==abs(j-k))) return false; return true; } void NQueens(int k,int n,int *x) //递归函数(求解n皇后问题) { for (int i=0;i<n;i++) { if(Place(k,i,x)) { x[k]=i; if (k==n-1) //输出所有可行解 { for (i=0;i<n;i++) cout<<x[i]<<" "; cout<<endl; } else { NQueens(k+1,n,x); } } } } void NQueens(int n,int *x) { NQueens(0,n,x); }
版权声明:本文为博主原创文章,未经博主允许不得转载。
posted on 2015-05-26 12:38 Tob__yuhong 阅读(793) 评论(0) 编辑 收藏 举报