输出格式
一行,即唯一的那组解。
解是这样表示的:输出NN个数字,分别表示A,B,C,…A,B,C,…所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
输入输出样例
输入 #1复制
5
ABCED
BDACE
EBBAA
输出 #1复制
1 0 3 4 2
参考以下DFS代码,可以摸索出一个DFS模板
void dfs(.....){
if(符合返回要求){
....
return;
}
if(符合剪枝要求){
.....
return;
}
for(枚举相关状态){
标记
记录
dfs(更深一层)
擦除标记
擦除记录
}
}
总结目录
1 本题的枚举
2 相关的细节
1 本题的枚举
本题的枚举中,我们需要处理多个情况。我们要考虑的是上面的数取什么,下面的数字取什么,然后具体的搭配是否合理,并且不能取重复的数字。这个逻辑是符合人的思维的,但是具体的代码书写还是比较复杂的。
首先是dfs需要的信息,我一开始dfs只取了最后一列,但是发现情况太多,根本列举不完。后来看了参考的代码后,发现还应该列举行的信息。因此,我们应该在dfs中加入行的信息和列的信息,并且还需要加入进位的标记。
那么有了这些标记,我们就要考虑如何进行深搜。首先是返回条件,比较简单,当列缩小到计算完毕,并且没有进位的时候就可以记录答案了,这个还是比较好写的。
其次是如何进行枚举的搜索。 我认为任何枚举,都是从数字开始枚举,然后利用数字去套我们的具体被枚举的物体。因此,对于最外层,一定是一个for循环枚举数字。当然,在这一题中,枚举当前的数字之前需要查看这个数字它是否已经被使用过了,如果已经有了,那么我们只要继续深搜就可以了。关键是如果这个数字没有,那么我们就要进行枚举了,也就是要进入for循环的枚举。
在这个问题中,本质其实是大的if判断是否要进入枚举,不枚举就可以深搜了。在进入枚举或者不枚举后,又分为要深搜更深一层还是跳跃到另外一个地方进行深搜。
2 剪枝相关的细节
本题中的剪枝,主要是使用的是判断是否符合要求,如果已经匹配出来的结果不符合了的话,那么继续计算下去是没有意义的,直接return就好了,也就实现了剪枝的处理。
代码
#include<iostream>
#include<cstring>
using namespace std;
int n;
int words[3][30];//记录下算式的值
int used[30];//由于每个字母有唯一值,因此利用hash进行查询是否已经被使用
int res[30];//用来枚举答案,0-25对应于A-Z即26个英文字母
int finalres[30];//用来记录最终答案
void dfs(int row,int col,int carry) {
//多个枚举应该以枚举数字为基础,然后去推进层数
if (col<0&&carry==0) {
for (int i = 0; i < n; i++) {
finalres[i] = res[i];
}
return;
}
for (int i = col; i >= 0; i--) {
//这个循环的存在是为了剪掉那些已经枚举过的,但是在后面计算中不符合的情况
int num1 = res[words[0][i]];
int num2 = res[words[1][i]];
int num3 = res[words[2][i]];
if (num1 == -1 || num2 == -1 || num3 == -1) continue;//这里当时的逻辑是怎么想到用continue的
int sum = num1 + num2;
if (sum%n != num3 && (sum + 1) % n != num3) return;//如果有一个不符合,那么一定是不合理的
}
if (res[words[row][col]] == -1) {
//如果当前值是不存在的,也就是说这个值是没有被填充的,那么我们要枚举这个值
for (int i = 0; i <n; i++) {
if (!used[i]) {//某个数字没有被使用,那么可以在当前层枚举这个数字,在if中可以对当前层的业务逻辑进行if分类
if (row != 2) {//如果没有枚举完毕,那么我们需要继续递增row的层数,直到3行全部出来才能计算
used[i] = 1;
res[words[row][col]] = i;
dfs(row + 1, col, carry);
used[i] = 0;
res[words[row][col]] = -1;
}
else if (row == 2) {
//这个分支仍然是要填充,只是填充的过程又加了剪枝(筛选),但是我觉得其实不用,因为前面已经剪掉了
int sum = res[words[0][col]] + res[words[1][col]] + carry;
if (sum%n != i)continue;
used[i] = 1;
res[words[row][col]] = i;
dfs(0, col - 1, sum / n);
used[i] = 0;
res[words[row][col]] = -1;
}
}
}
}
else if(res[words[row][col]]!=-1){
//如果当前的值已经有了,那就不需要遍历了,直接根据层数进行更深的遍历
if (row != 2) {
dfs(row + 1, col, carry);
}
else if (row == 2) {
int sum = res[words[0][col]] + res[words[1][col]] + carry;
if (sum%n != res[words[row][col]])return;
dfs(0, col - 1, sum / n);
}
}
}
void display() {
for (int i = 0; i < n; i++) {
if (i == 0) {
cout << finalres[i];
}
else {
cout << " " << finalres[i];
}
}
}
int main() {
memset(res, -1, sizeof(res));//置为-1,说明没有值,不可置为0,因为0也算有效的
cin >> n;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < n; j++) {
char ch;
cin >> ch;
words[i][j] = ch - 'A';//将字母转换为数字编号
}
}
dfs(0, n - 1, 0);
display();
return 0;
}