符号三角形(dfs+位运算)

符号三角形的 第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]);
		 }
}
}

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鱼爱吃火锅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值