[cpp]
view plain
copy
- /*回溯法解符号三角形问题
- 问题描述:
- 如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。
- – + + – + + +
- – + – – + +
- – – + – +
- + – – –
- – + +
- – +
- –
- 在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n,
- 计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。
- 解题思路:
- 1、不断改变第一行每个符号,搜索符合条件的解,可以使用递归回溯
- 为了便于运算,设+ 为0,- 为1,这样可以使用异或运算符表示符号三角形的关系
- ++为+即0^0=0, –为+即1^1=0, +-为-即0^1=1, -+为-即1^0=1;
- 2、因为两种符号个数相同,可以对题解树剪枝,
- 当所有符号总数为奇数时无解,当某种符号超过总数一半时无解
- 参考了学习资料,重新实现以练习,有疏漏之处敬请指正zd163boy@163.com。
- 杨小进,17:13 2009-8-5
- */
- #include”iostream”
- using namespace std;
- typedef unsigned char uchar;
- char cc[2]={‘+’,‘-‘}; //便于输出
- int n, //第一行符号总数
- half, //全部符号总数一半
- counter; //1计数,即“-”号计数
- uchar **p; //符号存储空间
- long sum; //符合条件的三角形计数
- //t,第一行第t个符号
- void Backtrace(int t)
- {
- int i, j;
- if( t > n )
- {//符号填充完毕
- sum++;
- //打印符号
- cout << “第” << sum << “个:”<<endl;
- for(i=1; i<=n; ++i)
- {
- for(j=1; j<i; ++j)
- {
- cout << ” “;
- }
- for(j=1; j<=n-i+1; ++j)
- {
- cout << cc[ p[i][j] ] << ” “;
- }
- cout <<endl;
- }
- }
- else
- {
- for(i=0; i<2; ++i)
- {
- p[1][t] = i; //第一行第t个符号
- counter += i; //“-”号统计
- for(j=2; j<=t; ++j) //当第一行符号>=2时,可以运算出下面行的某些符号
- {
- p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];//通过异或运算下行符号
- counter += p[j][t-j+1];
- }
- if( (counter <= half) && ( t*(t+1)/2 – counter <= half) )
- {//若符号统计未超过半数,并且另一种符号也未超过半数
- Backtrace(t+1); //在第一行增加下一个符号
- }
- //回溯,判断另一种符号情况
- for(j=2; j<=t; ++j)
- {
- counter -= p[j][t-j+1];
- }
- counter -= i;
- }
- }
- }
- int main()
- {
- cout << “请输入第一行符号个数n:”;
- cin >> n;
- counter = 0;
- sum = 0;
- half = n*(n+1)/2;
- int i=0;
- if( half%2 == 0 )
- {//总数须为偶数,若为奇数则无解
- half /= 2;
- p = new uchar *[n+1];
- for(i=0; i<=n; ++i)
- {
- p[i] = new uchar[n+1];
- memset(p[i], 0, sizeof(uchar)*(n+1));
- }
- Backtrace(1);
- for(i=0; i<=n; ++i)
- {
- delete[] p[i];
- }
- delete[] p;
- }
- cout << “/n总共 “ << sum << ” 个”<< endl;
- return 0;
- }
———————————————————————————————————————
———————————————————————————————————————
[cpp]
view plain
copy
- #include <iostream>
- using namespace std;
- class Triangle
- {
- private:
- int num;//第一行的符号数量
- int **p;//存储+ -号,0表示-,1表示+
- int count;//记录+号数量,根据+号数量,可以用i*(i+1)/2-count求出减号数量
- int sum;//记录+与-数量相等且等于num*(num+1)/4的情况
- const int half;//加号=减号=总符号数/2
- public:
- //构造函数
- Triangle(int num):half(num*(num+1)/4)
- {
- this->num=num;
- count=0;
- sum=0;
- p=new int* [num+1];
- for(int i=0;i<=num;i++)
- {
- p[i]=new int[num+1];//开辟一个num*num的矩阵,用于在回溯过程中记录三角形符号分布
- }
- }
- //调用回溯算法,并且先判断num*(num+1)/2是否为奇数
- void triangleSolve()
- {
- if(num*(num+1)/2%2==0)
- {
- triangle(1);
- display();
- }
- else
- {
- cout<<“不存在可能性”<<endl;
- return;
- }
- }
- //回溯核心算法
- void triangle(int i)
- {
- if(i>num)
- {
- ++sum;
- return;
- }
- for(int j=0;j<=1;j++)
- {
- p[1][i]=j;
- count+=j;
- for(int k=2;k<=i;k++)
- {
- p[k][i-k+1]=p[k-1][i-k+1]^p[k-1][i-k+2];//相同则为1,不同则为0,代表题目的规则.1为+,0为-
- count+=p[k][i-k+1];
- }
- if(count<=half&&i*(i+1)/2-count<=half)//剪枝函数,约束函数,如果当前的+,-都小于符号数一半,那么继续递归
- {
- triangle(i+1);
- }
- //恢复现场
- for(int k=2;k<=i;k++)
- {
- count-=p[k][i-k+1];//减去新加入右侧边的+符号数量
- }
- count-=j;//减去i位置的+数量
- }
- }
- void display()
- {
- cout<<“边长为”<<num<<“的情况下,一共有”<<sum<<“种符号三角形具有同等数量的+与-号”<<endl;
- }
- };
- void main()
- {
- Triangle test(7);//设置第一行有7个符号
- test.triangleSolve();//解决问题
- }
求+,-数量相等的三角形数量,整个三角形只与第一行的符号有关。
所以可以逐次安排第一行的每一个符号,由于每次安排好一个符号,那么就会在原确定的三角形最右侧加一条三角形边。 所以,利用回溯法,逐次对第一行每一个位置的符号做出选择,并且扩充新的三角形边。 为了实现回溯算法的优化,必须有剪枝函数,所以这里用+,-都小于总符号数的一半作为约束,只要能够递归到第n+1个字符,说明1到n个字符的安排都满足+<=half,-<=half,所以就是+与-数量相等,sum++。 为了在回溯过程中能够及时的计算出三角形的最新状况,所以设置了二维数组用于存储三角形,根据图形的关系可以求出三角形的右侧边加入到二维数组中。
转自:http://blog.csdn.net/qq120848369/article/details/5445282