符号三角形问题(回溯)

[cpp] 
view plain
copy

  1. /*回溯法解符号三角形问题 
  2.  
  3. 问题描述: 
  4. 如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。 
  5. – + + – + + +  
  6.  – + – – + +  
  7.   – – + – +  
  8.    + – – –  
  9.     – + +  
  10.      – +  
  11.       – 
  12. 在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n, 
  13. 计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。 
  14.  
  15. 解题思路: 
  16. 1、不断改变第一行每个符号,搜索符合条件的解,可以使用递归回溯 
  17. 为了便于运算,设+ 为0,- 为1,这样可以使用异或运算符表示符号三角形的关系 
  18. ++为+即0^0=0, –为+即1^1=0, +-为-即0^1=1, -+为-即1^0=1; 
  19.  
  20. 2、因为两种符号个数相同,可以对题解树剪枝, 
  21. 当所有符号总数为奇数时无解,当某种符号超过总数一半时无解 
  22.  
  23. 参考了学习资料,重新实现以练习,有疏漏之处敬请指正zd163boy@163.com。 
  24. 杨小进,17:13 2009-8-5  
  25. */  
  26. #include”iostream”  
  27. using namespace std;  
  28. typedef unsigned char uchar;  
  29.   
  30. char cc[2]={‘+’,‘-‘};   //便于输出  
  31. int n,                  //第一行符号总数  
  32.     half,               //全部符号总数一半  
  33.     counter;            //1计数,即“-”号计数  
  34.       
  35. uchar **p;              //符号存储空间      
  36. long sum;               //符合条件的三角形计数  
  37.   
  38. //t,第一行第t个符号  
  39. void Backtrace(int t)  
  40. {  
  41.     int i, j;  
  42.       
  43.     if( t > n )  
  44.     {//符号填充完毕  
  45.         sum++;  
  46.           
  47.         //打印符号  
  48.         cout << “第” << sum << “个:”<<endl;  
  49.         for(i=1; i<=n; ++i)  
  50.         {  
  51.             for(j=1; j<i; ++j)  
  52.             {  
  53.                 cout << ” “;  
  54.             }  
  55.               
  56.             for(j=1; j<=n-i+1; ++j)  
  57.             {  
  58.                 cout << cc[ p[i][j] ] << ” “;  
  59.             }  
  60.             cout <<endl;  
  61.         }  
  62.     }  
  63.     else  
  64.     {  
  65.        for(i=0; i<2; ++i)  
  66.        {  
  67.             p[1][t] = i;        //第一行第t个符号  
  68.             counter += i;       //“-”号统计  
  69.               
  70.             for(j=2; j<=t; ++j)  //当第一行符号>=2时,可以运算出下面行的某些符号  
  71.             {  
  72.                 p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];//通过异或运算下行符号  
  73.                 counter += p[j][t-j+1];                       
  74.             }  
  75.   
  76.             if( (counter <= half) && ( t*(t+1)/2 – counter <= half) )  
  77.             {//若符号统计未超过半数,并且另一种符号也未超过半数  
  78.                 Backtrace(t+1);         //在第一行增加下一个符号  
  79.             }  
  80.               
  81.             //回溯,判断另一种符号情况  
  82.             for(j=2; j<=t; ++j)    
  83.             {  
  84.                 counter -= p[j][t-j+1];  
  85.             }  
  86.                
  87.             counter -= i;  
  88.        }  
  89.     }  
  90. }  
  91.   
  92. int main()  
  93. {     
  94.     cout << “请输入第一行符号个数n:”;  
  95.     cin >> n;  
  96.     counter = 0;  
  97.     sum = 0;  
  98.     half = n*(n+1)/2;  
  99.     int i=0;  
  100.       
  101.     if( half%2 == 0 )  
  102.     {//总数须为偶数,若为奇数则无解  
  103.         half /= 2;  
  104.         p = new uchar *[n+1];  
  105.   
  106.         for(i=0; i<=n; ++i)  
  107.         {  
  108.            p[i] = new uchar[n+1];  
  109.            memset(p[i], 0, sizeof(uchar)*(n+1));  
  110.         }  
  111.              
  112.         Backtrace(1);  
  113.         for(i=0; i<=n; ++i)  
  114.         {  
  115.             delete[] p[i];  
  116.         }  
  117.         delete[] p;  
  118.     }  
  119.       
  120.     cout << “/n总共 “ << sum << ” 个”<< endl;  
  121.     return 0;  
  122. }  

———————————————————————————————————————

———————————————————————————————————————

[cpp] 
view plain
copy

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4. class Triangle  
  5. {  
  6. private:  
  7.     int num;//第一行的符号数量  
  8.     int **p;//存储+ -号,0表示-,1表示+  
  9.     int count;//记录+号数量,根据+号数量,可以用i*(i+1)/2-count求出减号数量  
  10.     int sum;//记录+与-数量相等且等于num*(num+1)/4的情况  
  11.     const int half;//加号=减号=总符号数/2  
  12. public:  
  13.     //构造函数  
  14.     Triangle(int num):half(num*(num+1)/4)  
  15.     {  
  16.         this->num=num;  
  17.         count=0;  
  18.         sum=0;  
  19.   
  20.         p=new int* [num+1];  
  21.         for(int i=0;i<=num;i++)  
  22.         {  
  23.             p[i]=new int[num+1];//开辟一个num*num的矩阵,用于在回溯过程中记录三角形符号分布  
  24.         }  
  25.     }  
  26.     //调用回溯算法,并且先判断num*(num+1)/2是否为奇数  
  27.     void triangleSolve()  
  28.     {  
  29.         if(num*(num+1)/2%2==0)  
  30.         {  
  31.             triangle(1);  
  32.             display();  
  33.         }  
  34.         else  
  35.         {  
  36.             cout<<“不存在可能性”<<endl;  
  37.             return;  
  38.         }  
  39.     }  
  40.     //回溯核心算法  
  41.     void triangle(int i)  
  42.     {  
  43.         if(i>num)  
  44.         {  
  45.             ++sum;  
  46.             return;  
  47.         }  
  48.   
  49.         for(int j=0;j<=1;j++)  
  50.         {  
  51.             p[1][i]=j;  
  52.             count+=j;  
  53.             for(int k=2;k<=i;k++)  
  54.             {  
  55.                 p[k][i-k+1]=p[k-1][i-k+1]^p[k-1][i-k+2];//相同则为1,不同则为0,代表题目的规则.1为+,0为-  
  56.                 count+=p[k][i-k+1];  
  57.             }  
  58.             if(count<=half&&i*(i+1)/2-count<=half)//剪枝函数,约束函数,如果当前的+,-都小于符号数一半,那么继续递归  
  59.             {  
  60.                 triangle(i+1);  
  61.             }  
  62.             //恢复现场  
  63.             for(int k=2;k<=i;k++)  
  64.             {  
  65.                 count-=p[k][i-k+1];//减去新加入右侧边的+符号数量  
  66.             }  
  67.             count-=j;//减去i位置的+数量  
  68.         }  
  69.     }  
  70.     void display()  
  71.     {  
  72.         cout<<“边长为”<<num<<“的情况下,一共有”<<sum<<“种符号三角形具有同等数量的+与-号”<<endl;  
  73.     }  
  74. };  
  75.   
  76. void main()  
  77. {  
  78.     Triangle test(7);//设置第一行有7个符号  
  79.     test.triangleSolve();//解决问题  
  80. }  

 

求+,-数量相等的三角形数量,整个三角形只与第一行的符号有关。

 

所以可以逐次安排第一行的每一个符号,由于每次安排好一个符号,那么就会在原确定的三角形最右侧加一条三角形边。 所以,利用回溯法,逐次对第一行每一个位置的符号做出选择,并且扩充新的三角形边。 为了实现回溯算法的优化,必须有剪枝函数,所以这里用+,-都小于总符号数的一半作为约束,只要能够递归到第n+1个字符,说明1到n个字符的安排都满足+<=half,-<=half,所以就是+与-数量相等,sum++。  为了在回溯过程中能够及时的计算出三角形的最新状况,所以设置了二维数组用于存储三角形,根据图形的关系可以求出三角形的右侧边加入到二维数组中。

转自:http://blog.csdn.net/qq120848369/article/details/5445282

    原文作者:回溯法
    原文地址: https://blog.csdn.net/chaoyuan899/article/details/8628237
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞