回溯法--符号三角形问题

问题描述: 
如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。 
- + + - + + +  
 - + - - + +  
  - - + - +  
   + - - -  
    - + +  
     - +  
      - 
在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n, 
计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。 
 
解题思路: 
1、不断改变第一行每个符号,搜索符合条件的解,可以使用递归回溯 
为了便于运算,设+ 为0,- 为1,这样可以使用异或运算符表示符号三角形的关系 
++为+即0^0=0, --为+即1^1=0, +-为-即0^1=1, -+为-即1^0=1;   
2、因为两种符号个数相同,可以对题解树剪枝, 
当所有符号总数为奇数时无解,当某种符号超过总数一半时无解

源代码:

#include"iostream"  
using namespace std;  
typedef unsigned char uchar;  
 
char cc[2]={‘+‘,‘-‘};   //便于输出  
int n,                  //第一行符号总数  
    half,               //全部符号总数一半  
    counter;            //1计数,即“-”号计数         
uchar **p;              //符号存储空间      
long sum;               //符合条件的三角形计数   
 
void Backtrace(int t)   //t,第一行第t个符号  
{  
    int i, j;          
    if( t > n )  
    {//符号填充完毕  
        sum++;              
        //打印符号  
        cout << "第" << sum << "个:\n";  
        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 << "\n";  
        }  
    }  
    else 
    {  
       for(i=0; i<2; ++i)  
       {  
            p[1][t] = i;        //第一行第t个符号  
            counter += i;       //“-”号统计,因为"+"的值是0 
              
            for(j=2; j<=t; ++j)  //当第一行符号>=2时,可以运算出下面行的某些符号,j可代表行号  
            {   
               p[j][t-j+1] = p[j-1][t-j+1]^p[j-1][t-j+2];//通过异或运算下行符号,t-j+1确定的很巧  
               counter += p[j][t-j+1];                       
            }    
            if( (counter <= half) && ( t*(t+1)/2 - counter <= half) )  
            {//若符号统计未超过半数,并且另一种符号也未超过半数,同时隐含两者必须相等才能结束  
                Backtrace(t+1);         //在第一行增加下一个符号  
            }                
            //回溯,判断另一种符号情况   像是出栈一样,恢复所有对counter的操作
            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;  
}

时间: 2024-10-25 14:11:41

回溯法--符号三角形问题的相关文章

回溯法之符号三角形问题

/*回溯法解符号三角形问题 问题描述: 如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”. - + + - + + + - + - - + + - - + - + + - - - - + + - + - 在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n, 计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同. 解题思路: 1.不断改变第一行每个符号,搜索符合条件的解,可以使用递归回溯 为了便于运算,设+

P1118 [USACO06FEB]数字三角形`Backward Digit Su`… 回溯法

有这么一个游戏: 写出一个11至NN的排列a_iai?,然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少11,直到只剩下一个数字位置.下面是一个例子: 3,1,2,43,1,2,4 4,3,64,3,6 7,97,9 1616 最后得到1616这样一个数字. 现在想要倒着玩这样一个游戏,如果知道NN,知道最后得到的数字的大小sumsum,请你求出最初序列a_iai?,为11至NN的一个排列.若答案有多种可能,则输出字典序最小的那一个. [

回溯法 -数据结构与算法

1.回溯法算法思想: 定义: 回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 1.回溯法适用:有许多问题,当需要找出它的解集(全部解)或者要求回答什么解是满足某些约束条件的最优解时,往往要使用回溯法. 2.有组织的穷举式搜索:回溯法的基本做法是搜索或者有的组织穷尽搜索.它能避免搜索所有的可能性.即避免不必要的搜索.这种方

算法设计与分析——回溯法算法模板

以深度优先方式系统搜索问题解的算法称为回溯法.在回溯法中,解空间树主要分为了四种子集树.排列树.n叉树和不确定树. 在<算法设计与分析课本>中介绍了11个回溯法的问题样例,这里根据解空间树的类型做一个分类. 子集树 装载问题 符号三角形问题 0-1背包问题 最大团问题 算法模板: void backtrack(int t) { if(搜索到叶子结点) { return; } for(i=0; i<=1; i++) //01二叉树 { if(满足约束函数和限界函数)//剪枝 { backt

经典算法学习之回溯法

回溯法的应用范围:只要能把待求解的问题分成不太多的步骤,每个步骤又只有不太多的选择就可以考虑使用回溯法. 若用回溯法求问题的所有解时,要回溯到根,且根结点的所有可行的子树都要已被搜索遍才结束. 而若使用回溯法求任一个解时,只要搜索到问题的一个解就可以结束. 回溯法将问题的候选解按照某一顺序逐一枚举和检验.当发现当前候选解不可能是解时,就选择下一个候选解,若当前候选解符合要求,且还未达到求解的规模,则继续扩展当前候 选解的规模,如果候选解已经满足了所有要求,并且也达到了问题的规模,那么该候选解就是

回溯法与树的遍历

关于回溯法和DFS做下总结: 在程序设计中有一类题目求一组解或者求全部解或者求最优解等系列问题,不是根据某种特定的规则来计算,而是通过试探和回溯的搜索来查找结果,通常都会设计为递归形式. 这类题本身是一颗状态树,当只有两种情况的时候则为二叉树,这棵树不是之前建立的,而是隐含在遍历过程中的.接下来根据一些题目来提高认识. 一.二叉状态树 题目: 说白了就是一个全遍历的过程,找出每一种可能的组合.对于123则有题目中的8种情况. 思路: 这样的全排列问题可以从元素本身入手,每一个元素只有两种状态,被

[LeetCode] 679. 24 Game(回溯法)

传送门 Description You have 4 cards each containing a number from 1 to 9. You need to judge whether they could operated through *, /, +, -, (, )to get the value of 24. Example 1: Input: [4, 1, 8, 7] Output: True Explanation: (8-4) * (7-1) = 24 Example 2

五大常用算法之四:回溯法

(转自:http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html) 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径. 回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”. 许

0-1背包-回溯法

算法描述: 0-1背包的回溯法,与装载问题的回溯法十分相似.在搜索解空间树时,只要其左儿子结点是一个可行结点,搜索就进入其左子树.当右子树中有可能包含最优解时才进入右子树进行搜索.否则将右子树剪去. 计算右子树上界的更好算法是: 将剩余物品依其单位重量价值排序,然后依次装入物品,直至装不下时,再装入该物品的一部分而装满背包. 算法实现: 由Bound函数计算当前节点处的上界. 类Knap的数据成员记录解空间树的节点信息,以减少参数传递及递归调用所需的栈空间. 在解空间树的当前扩展结点处,仅当要进