问题
符号三角形的 第1行有n个由“+”和”-“组成的符号 ,以后每行符号比上行少1个,2个同号下面是”+“,2个异 号下面是”-“ 。计算有多少个不同的符号三角形,使其所含”+“ 和”-“ 的个数相同 。 n=7时的1个符号三角形如下:
+ + - + - + +
+ - - - - +
- + + + -
- + + -
- + -
- -
+
Input
每行1个正整数n <=24,n=0退出.
Output
符号三角形的个数.
Sample Input
15
16
19
20
0
Sample Output
1896
5160
32757
59984
解析
当确定了三角形的第一行的符号以后,整个三角形就确定了。
确定一个分量三角形深度增加一层,是在已有的三角形右边增加了一条边
-
解向量:x[1:n]表示第一行
-
可行性的约束函数:当前三角形"+"和“-”都不超过符号总数的一半,即n*(n+1)/4
-
无解:n*(n+1)/2为奇数
-
解空间:子集树
第i个分量只有两种取值,1(”+“)和0(”-“)。
代码
符号x[idx]
有“+”和“-”两种情况,子集树是完全二叉树。
若idx>n,到达子集树的叶节点,得到一种可行解,ans++;
若idx<=n,符号三角形还没完全生成,计算x[idx]
的“+”和“-”两种情况。
#include <iostream>
#include<cstring>
using namespace std;
int n;//n表示符号个数
int p[100][100];//存储符号三角形矩阵
int cnt;//当前三角形的 + 个数
int ans;//+和-数量相同的三角形个数
void backtrack(int idx){
//剪枝
int half=n*(n+1)/2/2;
int minusNum=idx*(idx-1)/2 -cnt;//当前三角形的 - 个数
//+ 个数或者 - 个数超过了三角形的一半,则不可能是解
if(cnt>half||(minusNum > half))
return;
if(idx>n){
ans++;
return;
}
//遍历第idx层的两个子节点,0表示-,1表示+
for(int i=0;i<2;i++){
p[1][idx]=i;//第1行的第idx个元素
cnt+=i;//记录”+“的个数
//更新斜对角线,即矩阵接下来的2-idx行
for(int j=2;j<=idx;j++){
p[j][idx-j+1]=p[j-1][idx-j+1]^p[j-1][idx-j+2];
cnt+=p[j][idx-j+1];//当前三角形的 + 个数
}
backtrack(idx+1);
//撤销
for(int j=2;j<=idx;j++){
cnt-=p[j][idx-j+1];//当前三角形的 + 个数
}
cnt-=i;
}
}
void compute(int n){
memset(p,0,sizeof(p));
cnt=0;
ans=0;
int total=n*(n+1)/2;
if(total%2==1){//总符号数为奇数,不可能+和-数量相等
ans=0;
return;
}
backtrack(1);
}
int main()
{
while(cin>>n&&n!=0){
compute(n);
cout<<ans<<endl;
}
return 0;
}
/*
输入:
15
16
19
20
0
输出:
1896
5160
32757
59984
*/
复杂度:一共n个分量要确定,解空间树一共2n个结点。处理每一个结点的时候,结点的层数为idx,则需要对idx个元素的值进行计算。
T(n)=O(n * 2n)