回溯算法 --- 例题3.符号三角形问题


一.问题描述

下图是由14个“+”和14个“-”组成的符号三角形。2个同号下面都是“+”,2个异号下面都是“-”。在一般情况下,符号三角形的第一行有n个符号.目标是:符号三角形问题要求对于给定的n,计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。

image-20211109210223659 改为此: image-20211109210247846

二.解题思路

解向量用n元组x[1:n]表示符号三角形的第一行,每个元素均可取0, 1两个值
约束函数:

  • 当前符号三角形所包含的“+”个数与“-”个数均不超过 n*(n+1)/4

一个例子:第1行是4个字符的符号三角形的子集树

具体代码如下:

// 符号三角形回溯算法
#include
using namespace std;
class Triangle
{
    friend int Compute(int);
    private:
        void Backtrack(int t); //用类Triangle的数据成员记录解空间中结点信息,以减少传给Backtrack函数的参数.
        int n,          //第一行的符号个数
            half,       //n*(n+1)/4.
            count,      //当前'+'个数
            **p;        //符号三角形矩阵
        long sum;       //已找到的符号三角形数
};
void Triangle::Backtrack(int t)
{
    if((count>half) || t*(t-1)/2-count > half)  //表示'+'号没超过一半,'-'号也没超过一半!
        return;
    if(t>n) sum++;  //能到达叶节点,说明+,-各一半,可行解加一
    else 
    {
        for(int i=0; i<2; i++) //每个元素可以取两个值(每个分支所做的事情是一样的,所以直接用一个循环,不用分为左右)
        {
            p[1][t] = i;
            count += i;
            for(int j=2; j<=t; j++)  //每次只计算了对角线
            {
                p[j][t-j+1] = p[j-1][t-j+1] ^ p[j-1][t-j+2];  //只要第一行确定了,得到的符号三角形一定是唯一的!
                count += p[j][t-j+1];
            }
            Backtrack(t+1);
            for(int j=2; j<=t; j++)  //回溯,反操作
                count -= p[j][t-j+1];  
            count -= i;
        }
    }
}
int Compute(int n)
{
    Triangle X;
    X.n = n;
    X.count = 0;
    X.sum = 0;
    X.half = n*(n+1)/2;
    if(X.half%2 == 1) return 0;  //如果给出的n使得符号三角形的符号总个数为奇数,直接return 0
    X.half /= 2;
    int **p = new int*[n+1];
    for(int i=0; i<=n; i++)
        p[i] = new int[n+1];
    for(int i=0; i<=n; i++)
    {
        for(int j=0; j<=n; j++)
            p[i][j] = 0;
    }
    X.p = p;
    X.Backtrack(1);
    return X.sum;
}
int main()
{
    cout<<"请输入第一行符号的个数n: ";
    int n;
    while(cin>>n && n!=0)
    {
        cout<<"方案数: "<

运行结果如下:

参考毕方明老师《算法设计与分析》课件.

欢迎大家访问个人博客网站---乔治的编程小屋,和我一起加油吧!