符号三角形问题

来源:互联网 发布:雅典娜潮州网络女神 编辑:程序博客网 时间:2024/06/12 00:20
/*Name: 符号三角形问题 Copyright: free Author: 巧若拙 Date: 20-07-17 10:42Description: 问题描述: 如下图是由14个“+”和14个“-”组成的符号三角形, 2个同号下面都是“+”,2个异号下面都是“-”。 - + + - + + +   - + - - + +    - - + - +     + - - -      - + +       - +        - 在一般情况下,符号三角形的第一行有n个符号, 符号三角形问题要求对于给定的n, 计算有多少个不同的符号三角形,使其所含的“+”和“-”的个数相同。  解题思路: 比较直接的想法是先回溯生成第一行符号的所有可能情况,然后按照规律生成符号三角形,当某种符号超过总数一半时无解,直接返回。 因为每行符号都只由上一行符号决定,故可以用一维数组代替二维数组 */#include<iostream>#include<cmath>using namespace std;const int N = 20; //符号的最大个数char A[N];//记录符号三角形矩阵的第一行 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[N];//记录符号三角形矩阵的当前行 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:42Description: 问题描述: 如下图是由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[N][N];//记录符号三角形矩阵 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;} }