符号三角形题解

前言:今天的题是真的很 恶心 好,每道题都很有意思,其中第一题让我 浪费 用了一个下午,话不多说,进入正题

听说有大佬觉得我发题面出来在凑字数,那就不发了吧

符号三角形

注:这道题的数据其实没这么吓人,需要运算的最大的只有20.

看到题目,爆搜,不用想太多,先来试一试。(写的很 麻烦,后面的代码会慢慢变简洁,大佬勿喷)

#include <cstdio>
#define zong n * (n + 1) / 4

const int MAXN = 105;
int n;
int ans[MAXN];//ans[i]记录当n = i时的答案 
int sum0, sum1;//分别记录0(-)的个数,1(+)的个数 

bool map[MAXN][MAXN];

void dfs(int, int);//填充

int main() {
	while(1) {
		scanf("%d", &n);
		if(n == 0) break;
		printf("%d ", n);
		if(ans[n] != 0) printf("%d\n", ans[n]);
		else {
			dfs(1, 1);
			printf("%d\n", ans[n]);
		}
	}
	return 0;
}

void dfs(int x, int y) {
	if(x == n + 1 && y == 1) {//跳出来 
		if(sum0 == sum1) ans[n]++;
		return;
	} 
	if(x == 1) {//第一行,随便填 
		if(y == n - x + 1) {//这一行的最后一个数 
			map[x][y] = 0;//这个数若是-
			sum0++;
			dfs(x + 1, 1);
			sum0--;
			
			map[x][y] = 1;//这个数若是+ 
			sum1++;
			dfs(x + 1, 1);
			sum1--;
		}
		else {//不是最后一个数 
			map[x][y] = 0;//这个数若是-
			sum0++; 
			dfs(x, y + 1);
			sum0--;
			
			map[x][y] = 1;//这个数若是+
			sum1++;
			dfs(x, y + 1);
			sum1--;
		} 
	}
	
	else {//不是第一行 
		if(y == n - x + 1) {//这一行的最后一个数 
			if(map[x - 1][y] == map[x - 1][y + 1]) {//推出这个数是+
				map[x][y] = 1; 
				sum1++;
				dfs(x + 1, 1);
				sum1--;
			}
			else {//推出这个数是- 
				map[x][y] = 0;
				sum0++; 
				dfs(x + 1, 1);
				sum0--;
			}
		}
		else {//不是最后一个数 
			if(map[x - 1][y] == map[x - 1][y + 1]) {//推出这个数是+ 
				map[x][y] = 1;
				sum1++;
				dfs(x, y + 1);
				sum1--;
			}
			else {//推出这个数是-
				map[x][y] = 0;
				sum0++; 
				dfs(x, y + 1);
				sum0--;
			}
		}
	}
	return;
} 

如果你是这样交的,你会得到郭老师给你的奖励— Time Limit Exceeded
恭喜恭喜

肯定是要剪枝的,我们再想一想剪枝的方法,注意“所含”+“ 和”-“ 的个数相同”这句话十分重要。

  • 1.要‘+’和‘-’的个数相同,辣么,如果‘+’和‘-’的总数和是一个奇数,可 能使它们的个数相同吗?不能吧,而‘+’和‘-’的总数和就是这个三角形的总数,那就有剪枝的一个条件了。
if(n * (n + 1) / 2 % 2 == 1) printf("0\n");
  • 2要‘+’和‘-’的个数相同,辣么,如果‘+’或‘-’的个数超过了一半(注意是超过‘>’),那也知道它不行,这样又有剪枝的一个条件了。
//sum0表示0(-)的个数,sum1同理
if(sum0 > zong) return;
if(sum1 > zong) return;
#include <cstdio>
#define zong n * (n + 1) / 4

const int MAXN = 105;
int n;
int ans[MAXN];//ans[i]记录当n = i时的答案 
int sum0, sum1;//分别记录0(-)的个数,1(+)的个数 

bool map[MAXN][MAXN];

void dfs(int, int);//填充

int main() {
	while(1) {
		scanf("%d", &n);
		if(n == 0) break;
		printf("%d ", n);
		if(n * (n + 1) / 2 % 2 == 1) printf("0\n"); 
		else {
			if(ans[n] != 0) printf("%d\n", ans[n]);
			else {
				dfs(1, 1);
				printf("%d\n", ans[n]);
			}
		}
	}
	return 0;
}

void dfs(int x, int y) {
	if(x == n + 1 && y == 1) {//跳出来 
		if(sum0 == sum1) ans[n]++;
		return;
	} 
	if(sum0 > zong) return;//剪枝
	if(sum1 > zong) return;//剪枝 
	if(x == 1) {//第一行,随便填 
		if(y == n - x + 1) {//这一行的最后一个数 
			map[x][y] = 0;//这个数若是-
			sum0++;
			dfs(x + 1, 1);
			sum0--;
			
			map[x][y] = 1;//这个数若是+ 
			sum1++;
			dfs(x + 1, 1);
			sum1--;
		}
		else {//不是最后一个数 
			map[x][y] = 0;//这个数若是-
			sum0++; 
			dfs(x, y + 1);
			sum0--;
			
			map[x][y] = 1;//这个数若是+
			sum1++;
			dfs(x, y + 1);
			sum1--;
		} 
	}
	
	else {//不是第一行 
		if(y == n - x + 1) {//这一行的最后一个数 
			if(map[x - 1][y] == map[x - 1][y + 1]) {//推出这个数是+
				map[x][y] = 1; 
				sum1++;
				dfs(x + 1, 1);
				sum1--;
			}
			else {//推出这个数是- 
				map[x][y] = 0;
				sum0++; 
				dfs(x + 1, 1);
				sum0--;
			}
		}
		else {//不是最后一个数 
			if(map[x - 1][y] == map[x - 1][y + 1]) {//推出这个数是+ 
				map[x][y] = 1;
				sum1++;
				dfs(x, y + 1);
				sum1--;
			}
			else {//推出这个数是-
				map[x][y] = 0;
				sum0++; 
				dfs(x, y + 1);
				sum0--;
			}
		}
	}
	return;
} 

这样提交你会发现:你还是得到了郭老师的奖励,这是为什么呢?我也不知道,网上查了查,函数调用时会耗时的,而这个代码函数调用次数过多,就容易超时(不知道还有没有其他原因,等待大佬的支援QAQ)。

最开始我不知道什么原因导致超时,就请求LJS大佬的支援,大佬就给出了一个不一样的解法,从下往上搜索,枚举第一列的所有情况(其实和之前的方法是大同小异的,而函数调用次数会少很多,这样就会节约时间呢qwq)。

同样的,剪枝还是可以用,也是同样的方法。

#include <cstdio>
#define zong n * (n + 1) / 2

const int MAXN = 105;
int n;
int ans[MAXN];//ans[i]记录当n = i时的答案 
int sum;//记录1(+)的个数

bool map[MAXN][MAXN];

void dfs(int);//填充

int main() {
	while(1) {
		scanf("%d", &n);
		if(n == 0) break;
		printf("%d ", n);
		if(n * (n + 1) / 2 % 2 == 1) printf("0\n"); 
		else {
			if(ans[n] != 0) printf("%d\n", ans[n]);
			else {
				dfs(1);
				printf("%d\n", ans[n]);
			}
		}
	}
	return 0;
}

void dfs(int x) {//在从下往上数的第x行		
	if(sum > zong) return;
	if(x == n + 1) {
		if(sum * 2 == zong) ans[n]++;
		return;
	}
	for(int i = 0; i <= 1; i++) {//填0或1  
		map[x][1] = i;
		sum += i;//若是1(+)则sum+1 
		for(int j = 2; j <= x; j++) {//确定了第一个数和下面一行,算出后面的符号
//			printf("%d %d %d\n", x, map[x][j - 1], map[x - 1][j - 1]);
			if(map[x][j - 1] == map[x - 1][j - 1]) {//是1(+) 
				map[x][j] = 1;
				sum ++;
			}
			else
				map[x][j] = 0;
		}
		
//		for(int j = 1; j <= x; j++) {
//			for(int k = 1; k <= j; k++)
//				printf("%3d", map[j][k]);
//			printf("\n");
//		}
//		printf("\n");
//		printf("%d\n", sum);
//		printf("\n\n\n");
		
		dfs(x + 1);//继续填 
		sum -= i;
		for(int j = 2; j <= x; j++) {
			if(map[x][j]) {
				sum--;
				map[x][j] = 0;
			}
		}
	}
	return;
}

给出解法后,LJS大佬就提醒我可以用位运算,既可以加快速度,还可以让代码简化,并吐槽了我的代码写的很难看 我也这样想呢

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值