前言:今天的题是真的很 恶心 好,每道题都很有意思,其中第一题让我 浪费 用了一个下午,话不多说,进入正题
听说有大佬觉得我发题面出来在凑字数,那就不发了吧
注:这道题的数据其实没这么吓人,需要运算的最大的只有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大佬就提醒我可以用位运算,既可以加快速度,还可以让代码简化,并吐槽了我的代码写的很难看 我也这样想呢