回溯法-符号三角形

问题描述:如下图所示,由14个‘+’和14个’-‘号组成的符号三角形。2个同号下面是’+’,2个异号下面是‘-’号。

《回溯法-符号三角形》

在一般情况下,符号三角形的第一行有n个符号。求:给定的n,计算有多少个不同的符号三角形,使其所含的’+’和’-‘的个数相同。

《回溯法-符号三角形》

问题分析

回溯法的经典的两种解空间树:子集树(从集合中选取特定的子集)和排列树(组合排列)。此题也属于组合排列问题,由于此题需要统计‘+’号和’-‘号的个数,因此需要存储每种情况下的中间结果。注意到当我们确定了第一行第i个符号后,我们可以很快得到第i+1个符号的结果,此时第i+1个符号或者为‘-’,或者为‘+’,当i = n时,即为所求结果。

为了加速处理,这里当第i位为‘+’时,x[i] = 0,否则x[i] = 1。为什么这么设置呢?因为题目中“2个同号下面是’+’,2个异号下面是‘-’号”这正是异或运算的性质哦,这样程序中的判断两个符号是否一致就可以采用异或运算,加快处理。

采用回溯法,还要注意限界函数的设计。由于符号三角形的每行之间相差1,是一个等差数列,所以符号三角形的总符号数为n*(n + 1)/2,并且要求’+’和’-‘号的个数相等,所以’+’和‘-’号的个数均为n*(n + 1) / 4。因此当’+‘或’-‘的个数大于n*(n + 1) / 4时,则停止并返回。最后n* (n + 1)/2必须为偶数,当为奇数时,‘+’和‘-’号的个数必不相等。

源码

struct tag_Triangle

{

int count; //‘+’号个数

int ** sym;//符号三角形矩阵

        int sum; //已找到符合条件的三角形数

};

void GetTriangleSum(int cur,int n,int half,struct tag_Triangle* tri)

{

//条件检测

if(!tri || tri->count > half / 2 || half – tri->count > half) return ;

if( cur > n) tri->sum ++;

else

{

for(int i = 0; i < 2; ++i)

{

tri->sym[1][cur] = i;

tri->count += i;

for(int j = 2; j <= cur; ++ j)

{

p[j][cur – j + 1]  =  p[j – 1][cur – j + 1] ^ p[j – 1][cur – j + 2];

tri->count += p[j][cur- j + 1];

}

GetTriangleSum(cur + 1,n,half,tri);

for(int j = 2; j <= cur; ++j)

{

tri->count -= p[j][cur – j + 1];

}

tri->count -= i;

}

}

void Init(int n)

{

if(n <= 0 ) return ;

int half =  n * (n + 1) / 2;

if( half & 0x01) return ;

struct tag_Triangle* tri = (struct tag_Triangle*)malloc(sizeof(struct tag_Triangle));

if(!tri) return ;

tri->sym = (int**)malloc(sizeof(int*) * (n + 1));

for(int i = 0; i <= n; ++i)

{

tri->sym[i] = (int*)malloc(sizeof(int) * (n + 1));

}

for(int i = 0; i <= n; ++ i)

for(int j = 0; j <= n; ++j)

tri->sym[i][j] = 0;

tri->sum = 0;

tri->count = 0;

GetTriangleSum(1,n,half,tri);

for(int i = 0; i <= n; ++i)

{

free(tri->sym[i]);

}

free(tri->sym);

free(tri);

}

最后说明这里的辅助空间可以设置为n*(n + 1) ./ 2,使用一维数组模拟二维数组。

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