您的位置:首页 > 其它

符号三角形问题

2017-07-20 13:09 225 查看
/*
Name: 符号三角形问题
Copyright: free
Author: 巧若拙
Date: 20-07-17 10:42
Description: 问题描述:
如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。
- + + - + + +
- + - - + +
- - + - +
+ - - -
- + +
- +
-
在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n,
计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。

解题思路:

比较直接的想法是先回溯生成第一行符号的所有可能情况,然后按照规律生成符号三角形,
当某种符号超过总数一半时无解,直接返回。
因为每行符号都只由上一行符号决定,故可以用一维数组代替二维数组
*/
#include<iostream>
#include<cmath>

using namespace std;

const int N = 20; //符号的最大个数
char A
;//记录符号三角形矩阵的第一行
int sum = 0;//保存可以放置的方案数
int n, half;

void Backtrace(int t); //递归回溯
void Solve();

int main()
{
n = 7;
half = n*(n+1)/2;
if (half%2 == 1)
{
cout << "无解" << endl;
return 0;
}
half /= 2;

Backtrace(0);
cout << sum << endl;

// system("pause");
return 0;
}

void Backtrace(int t) //递归生成第1行的n个符号
{
if (t == n)//第一行符号生成完毕,构造三角形并判断是否有解
Solve();
else //枚举第t个符号的两种取值,并递归生成下一个符号
{
A[t] = '+'; Backtrace(t+1);
A[t] = '-'; Backtrace(t+1);
}
}

void Solve()
{
char B
;//记录符号三角形矩阵的当前行
int s1 = 0, s2 = 0;//分别记录+和-的数量

for (int i=0; i<n; i++)//复制并累计第0行符号+和-的数量
{
B[i] = A[i];
if (B[i] == '+')
s1++;
else
s2++;
}

for (int i=1; i<n; i++)//第i行符号
{
for (int j=0; j<n-i; j++)//生成第i行符号,共n-i个
{
B[j] = (B[j]==B[j+1]) ? '+' : '-';

if (B[j] == '+')
s1++;
else
s2++;

if (s1 > half || s2 > half) //+或-超过半数即返回(无解)
return;
}
}
if (s1 == s2)
sum++;
}

/*
Name: 符号三角形问题
Copyright: free
Author: 巧若拙
Date: 20-07-17 10:42
Description: 问题描述:
如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。
- + + - + + +
- + - - + +
- - + - +
+ - - -
- + +
- +
-
在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n,
计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。

解题思路:
1、不断改变第一行每个符号,搜索符合条件的解,可以使用递归回溯
为了便于运算,设+ 为0,- 为1,这样可以使用异或运算符表示符号三角形的关系
++为+即0^0=0, --为+即1^1=0, +-为-即0^1=1, -+为-即1^0=1;
2、因为两种符号个数相同,可以对题解树剪枝,
当所有符号总数为奇数时无解,当某种符号超过总数一半时无解

本算法采用了两个技巧:一是用0和1代替+和-,这样可以用异或运算生成新的符号,同时可以快速记录符号1的数量;
二是回溯时每次只增加第一行的一个符号,然后利用异或运算规律生成对应的三角形(每次每行只增加一个符号,刚好形成一条斜线),
这样无需把全部三角形都生成后再判断符号个数,大大提高了效率。
*/
#include<iostream>
#include<cmath>

using namespace std;

const int N = 20; //符号的最大个数
int map

;//记录符号三角形矩阵
int sum = 0;//保存可以放置的方案数
int n, half;
int s1;//记录1(即-)的数量,0的数量即总数减去1的数量

void Backtrace(int t); //递归回溯

int main()
{
n = 7;
half = n*(n+1)/2;
if (half%2 == 1)
{
cout << "无解" << endl;
return 0;
}

half /= 2;

Backtrace(0);
cout << sum << endl;

// system("pause");
return 0;
}

void Backtrace(int t) //递归回溯,t表示第t行
{
if (s1 > half || t*(t+1)/2-s1 > half) //1或0超过半数即返回(无解)
return;
if (t == n)//符号三角形矩阵构造完毕,增加一个解
{
sum++;
cout << sum << endl;
for (int r=0; r<n; r++)
{
for (int k=0; k<n-r; k++)
{
cout << map[r][k];
}
cout << endl;
}
cout << endl;
return;
}
for (int i=0; i<2; i++)//符号只有0和1两种选择
{
map[0][t] = i; //生成第0行的第t个符号(下标从0开始)
s1 += i; //使用了一个技巧:记录符号1的数量,若为符号0,则不会累积
//根据第0行的符号,按异或运算规律从第1行开始生成新的符号,第0行每次增加一个符号,则按照从右上到左下斜线的顺序在每行都生成一个新的符号
for (int j=1; j<=t; j++)
{
map[j][t-j] = map[j-1][t-j]^map[j-1][t-j+1]; //按斜线顺序生成一个新符号
s1 += map[j][t-j];
}

Backtrace(t+1);
//还原
for (int j=1; j<=t; j++)
{
s1 -= map[j][t-j];
}
s1 -= i;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: