NOIP2004 虫食算 DFS

题目链接

http://noi-test.zzstep.com/contest/0x29%E3%80%8C%E6%90%9C%E7%B4%A2%E3%80%8D%E7%BB%83%E4%B9%A0/2902%20%E8%99%AB%E9%A3%9F%E7%AE%97

分析

剪枝的核心在于充分利用加法的性质,当某列第三行不是上两行之和且不是上两行之和加上可能的进位 1 1 1 时,剪枝。

首先按照字母的从右到左,从上到下的出现顺序依次搜索每个字母代表的数字。

枚举数字从大到小,以尽快产生进位。

AC代码

#include <cstdio>
#include <cstdlib>
#include <cstring>

const int maxn = 30;

int n, a[5][maxn], order[maxn], vis[maxn], num[maxn];
char s[5][maxn];

inline int judge() {
	int r = 0;
	for (int i = n; i; --i) {
		int n1 = num[a[1][i]], n2 = num[a[2][i]], n3 = num[a[3][i]];
		if (n3 != (n1 + n2 + r) % n) return 0;
		r = (n1 + n2 + r) / n;
	}
	return 1;
}

void dfs(int x) {
	if (num[a[1][1]] + num[a[2][1]] >= n) return;
	for (int i = n; i; --i) {
		int n1 = num[a[1][i]], n2 = num[a[2][i]], n3 = num[a[3][i]];
		if (n1 == -1 || n2 == -1 || n3 == -1) continue;
		if (n3 != (n1 + n2) % n && n3 != (n1 + n2 + 1) % n) return;
	}
	if (x == n + 1) {
		if (judge()) {
			printf("%d", num[0]);
			for (int i = 1; i < n; ++i) printf(" %d", num[i]);
			exit(0);
		}
		return;
	}
	for (int i = n - 1; i >= 0; --i) {
		if (vis[i]) continue;
		vis[i] = 1, num[order[x]] = i;
		dfs(x + 1);
		vis[i] = 0, num[order[x]] = -1;
	}
}

int main() {
	scanf("%d", &n);
	int p = 0;
	for (int i = 1; i <= 3; ++i) {
		scanf("%s", s[i] + 1);
		for (int j = 1; j <= n; ++j) a[i][j] = s[i][j] - 'A';
	}
	for (int i = n; i; --i)
		for (int j = 1; j <= 3; ++j)
			if (!vis[a[j][i]]) vis[a[j][i]] = 1, order[++p] = a[j][i];
	memset(vis, 0, sizeof(vis));
	memset(num, -1, sizeof(num));
	dfs(1);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值