符号三角形的 第1行有n个由“+”和”-“组成的符号 ,以后每行符号比上行少1个,2个同号下面是”+“,2个异 号下面是”-“ 。计算有多少个不同的符号三角形,使其所含”+“ 和”-“ 的个数相同 。 n=7时的1个符号三角形如下:
+ + - + - + +
+ - - - - +
- + + + -
- + + -
- + -
- -
+
Input
每行1个正整数n <=24,n=0退出.
Output
n和符号三角形的个数.
Sample Input
15
16
19
20
0
Sample Output
15 1896
16 5160
19 32757
20 59984
思路:一开始是没有思路的,搜了搜博客,千篇一律,而且还有很多错的博客,也不知道怎么写的
放置+和-的规则和运算异或差不多
异或规则大家都知道吧:相同为0;不同为1
相同放置+,不同放置- ;所以用0代替+,用1代替-;
++ -- ->+ = 0^0 1^1->0 +- -+-->- = 1^0 0^1 -> 1
所以求下一行元素的时候:a[j][t-j]=a[j-1][t-j]^a[j-1][t-j+1]
在图中的位置放置就是这样:
a[j-1][t-j] a[j-1][t-j+1]
a[j][t-j]
放置的思路--先放第一行,然后根据上边的异或式,求出下一行;
这期间用count记录负号(即1)的个数: 放置加减号的过程中,加号和减号的数目肯定不能超过总和的1/2(即sum/2);
剪枝:因为加号和减号数目相等,所以sum肯定是偶数,深搜之前先判断 sum=i*(i+1)/2是否为偶数
虽然如此还是会超时,所以都求出来存到一个sum[]数组中打表
思路代码:
import java.util.Arrays;
import java.util.Scanner;
public class Main{
static final int max=30;
static int sum[]=new int[max];
static int num[]=new int[max];
static int a[][]=new int [max][max];
static int half;//+和-总数量的一半
static int count;//记录负数的个数即1的个数
public static void dfs(int t,int n){
if(count>half||t*(t+1)/2-count>half) return;//往图形中添加+或-,两者的数量都不能超过总和的1/2
if(t==n){
sum[n]++; return;
}
for(int i=0;i<2;i++){
a[0][t]=i;//从第一行开始放+或-
count+=i;
for(int j=1;j<=t;j++){//放置下一行
a[j][t-j]=a[j-1][t-j]^a[j-1][t-j+1];
count+=a[j][t-j];
}
dfs(t+1,n);
for(int j=1;j<=t;j++){//回溯,判断另一种符号情况像是出栈一样,恢复所有对count的操作
count-=a[j][t-j];
}
count-=i;
}
}
public static void main(String[] args){
Scanner scan=new Scanner(System.in);
Arrays.fill(sum, 0);
for(int i=1;i<=24;i++){
int s=0;
s=i*(i+1)/2;//剪枝因为偶数和奇数相等,所以总和一定是偶数
if(s%2==0){
for(int j=0;j<25;j++)
Arrays.fill(a[j], 0);
count=0;
half=i*(i+1)/4;
dfs(0,i);
}
}
for(int i=0;i<=24;i++){
System.out.print(sum[i]+",");
}
System.out.println();
}
}
打表:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int sum[]={0,0,0,4,6,0,0,12,40,0,0,171,410,0,0,1896,5160,0,0,32757,59984,0,0,431095,822229};
Scanner scan=new Scanner(System.in);
while(scan.hasNext()){
int n=scan.nextInt();
if(n==0) break;
System.out.println(n+" "+sum[n]);
}
}
}