005-3算法笔记【回溯】符号三角形

问题

符号三角形的 第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

解析

当确定了三角形的第一行的符号以后,整个三角形就确定了。
在这里插入图片描述
确定一个分量三角形深度增加一层,是在已有的三角形右边增加了一条边
在这里插入图片描述

  1. 解向量:x[1:n]表示第一行

  2. 可行性的约束函数:当前三角形"+"和“-”都不超过符号总数的一半,即n*(n+1)/4

  3. 无解:n*(n+1)/2为奇数

  4. 解空间:子集树
    第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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值