洛谷 P1092 虫食算 dfs优化 (尚贤)

题目:
https://www.luogu.com.cn/problem/P1092

**爸爸的话:**孩子第一次做 提高/省选 的题目,在3个多小时的坚持下,还是80分,不过孩子没有崩溃,还打算用算法宝典里面另外一个思路从零开始,努力和心态都很值得点赞。
最后在老师的点拨下,找到AC的办法,从80到100分,是需要靠幸运,也是一个经验和经历。

记录这一刻。

孩子的AC代码,注意第十九行,之前是 for (int i = 0; i < n; ++i)
只有80分,过不了。这就是幸运的加分。#_#!

#include <iostream>
#include <cstdio>
#include <algorithm>
#define SIZE 26 + 10
using namespace std;
int n, number[SIZE + 65], next[SIZE], top;
char a[SIZE], b[SIZE], c[SIZE];
bool vis[SIZE + 65];

void dfs(const int &);
bool pd();
bool can();
void get(const char &);

int main() {
	freopen("cpp.in", "r", stdin);
	freopen("cpp.out", "w", stdout);
	scanf("%d%s%s%s", &n, &a, &b, &c);
	for (int i = n-1; i >= 0; --i) {
		get(a[i]);
		get(b[i]);
		get(c[i]);
		number[i + 65] = -1;
	}
	for (int i = 0; i < n; ++i) {
		vis[i] = false;
	}
	dfs(0);
	return 0;
}

void dfs(const int &t) {
	if (can()) {
		return;
	}
	if (t == n) {
		if (pd()) {
			for (int i = 0; i < n; ++i) {
				printf("%d ", number[i + 65]);
			}
			exit (0);
		}
		return;
	}
	for (int i = n - 1; i >= 0; --i) {
		if (!vis[i]) {
			vis[i] = true;
			number[next[t]] = i;
			dfs(t + 1);
			number[next[t]] = -1;
			vis[i] = false;
		}
	}
}
bool pd() {
	int x = 0;
	for (int i = n - 1; i >= 0; --i) {
		int A = number[a[i]], B = number[b[i]], C = number[c[i]];
		if ((A + B + x) % n != C) {
			return false;
		}
		x = (A + B + x) / n;
	}
	return true;
}
bool can() {
	if (number[a[0]] + number[b[0]] >= n) {
		return true;
	}
	for (int i = n - 1; i >= 0; --i) {
		int A = number[a[i]], B = number[b[i]], C = number[c[i]];
		if (A == -1 || B == -1 || C == -1) {
			continue;
		}
		if ((A + B) % n != C && (A + B + 1) % n != C) {
			return true;
		}
	}
	return false;
}
void get(const char &ch) {
	if (!vis[ch]) {
		vis[ch] = true;
		next[top++] = ch;
	}
}

题解: https://www.luogu.com.cn/problemnew/solution/P1092

这个题官方正解是高斯消元,可是我不会啊QAQ。
说一下搜索怎么做

这个题目第一个难点在于你要理解 nn 进制的加法

nn 进制的加法就是在十进制的基础上满十进一改成满nn 进一

由于这道题只考虑加法,所以进位只可能是 11 ,证明小学生都会,略

搜索的大体思路就是从第 11 位的值开始搜,搜到最后一位,判断是否合法

考虑剪枝

33 个字符串的长度都是 nn ,由此可以想到一个最简单的剪枝

最高位不能有进位

如果有进位,显然第 33 个串的长度不会是 nn ,而是n+1n+1,这并不合法

一个剪枝显然不够啊,再想一个

文字不太好描述,我们看图(不会用latex写竖式啊QAQ)
在这里插入图片描述

qwq

假设这是十进制下的加法,怎么判断这个竖式对不对?

显然这个竖式是错误的,因为个位上(8+6) mod 10=4 \not=5(8+6)mod10=4̸=5
由此推广到每一位,但是还要考虑进位,不慌,看另一张图
在这里插入图片描述

qwq

这个竖式是对的还是错的?

这并不好判断,虽然(8+6) mod 10=4 \not=5(8+6)mod10=4̸=5,但是这是中间位,有可能有进位

如果有进位, 那么(8+6+1) mod 10=5(8+6+1)mod10=5,这一位就是合法的了。

综合上面的分析,得到了另一个剪枝方法

用 AA 和 BB 表示两个加数,用 CC 表示两个加数的和

如果某 ii 位,满足(A[i]+B[i])mod;n\not=Cimodn̸=C[i] 和 (A[i]+B[i]+1)mod;n\not=Cimodn̸=C[i]
根据上面的分析,这种状态肯定不对,直接 returnreturn 就好了

或许还有别的剪枝,但是这两个应该够用了

我的代码里还用了一个玄学的 nextnext 数组,有什么用照着样例手推一遍就知道了,比较好理解。实在看不懂可以私信我qwq

学习科学,实用玄学——某钟姓dalao

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值