问题描述:如下图所示,由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,使用一维数组模拟二维数组。