符号三角形问题(回溯)

[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


  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是用回溯法解决符号三角形问题的C语言代码: ```c #include <stdio.h> #define MAX_N 10 int n; char triangle[MAX_N][MAX_N * 2 - 1]; char result[MAX_N][MAX_N * 2 - 1]; int check(int row, int col) { int i, j; // 检查当前位置是否已经填上符号 if (result[row][col] != ' ') { return 0; } // 检查当前符号是否已经在本行出现过 for (j = 0; j < col; j++) { if (result[row][j] == triangle[row][col]) { return 0; } } // 检查当前符号是否已经在本列出现过 for (i = 0; i < row; i++) { if (result[i][col] == triangle[row][col]) { return 0; } } // 检查当前符号是否与左上角和右上角的符号满足运算关系 if (row > 0 && col > 0) { switch (result[row - 1][col - 1]) { case '+': if (result[row][col] <= result[row - 1][col] || result[row][col] <= result[row][col - 1]) { return 0; } break; case '-': if (result[row][col] >= result[row - 1][col] || result[row][col] >= result[row][col - 1]) { return 0; } break; case '*': if (result[row][col - 1] == ' ' || result[row - 1][col] == ' ') { return 0; } if (result[row][col] <= result[row - 1][col] * result[row][col - 1] || result[row][col] <= result[row - 1][col] + result[row][col - 1]) { return 0; } break; case '/': if (result[row][col - 1] == ' ' || result[row - 1][col] == ' ' || result[row - 1][col] == 0 || result[row][col - 1] == 0) { return 0; } if (result[row][col] != result[row - 1][col] * result[row][col - 1] / result[row - 1][col] && result[row][col] != result[row - 1][col] + result[row][col - 1]) { return 0; } break; } } return 1; } void solve(int row, int col) { int i; if (row == n) { // 找到了符合要求的解,输出结果 for (i = 0; i < n; i++) { printf("%s\n", result[i]); } printf("\n"); return; } if (col == 2 * n - 1) { // 当前行已经填满,继续填下一行 solve(row + 1, 0); return; } if (triangle[row][col] != ' ') { // 当前位置已经有符号,直接尝试填下一个位置 solve(row, col + 1); return; } // 尝试填入所有可能的符号 result[row][col] = '+'; if (check(row, col)) { solve(row, col + 1); } result[row][col] = '-'; if (check(row, col)) { solve(row, col + 1); } result[row][col] = '*'; if (check(row, col)) { solve(row, col + 1); } result[row][col] = '/'; if (check(row, col)) { solve(row, col + 1); } result[row][col] = ' '; } int main() { int i; scanf("%d", &n); for (i = 0; i < n; i++) { scanf("%s", triangle[i]); } // 初始化结果矩阵 for (i = 0; i < n; i++) { int j; for (j = 0; j < 2 * n - 1; j++) { result[i][j] = ' '; } } solve(0, 0); return 0; } ``` 该代码使用了递归函数 `solve` 来进行搜索,并使用一个 `check` 函数来判断当前位置填入的符号是否符合要求。在 `solve` 函数中,首先判断当前行和当前位置是否已经填满,然后尝试填入所有可能的符号,如果符合要求,则继续递归调用 `solve` 函数,否则回溯到上一个状态,继续尝试其他符号。在找到符合要求的解时,将结果输出即可。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值