题目:https://www.luogu.org/problemnew/show/P1092
思路:
1、如果将n个字符对应的数字,取一个全排列后再检验,肯定会TLE。放弃。
2、为叙述方便,将三个数看作矩阵a,个位为第0列,被加数为第0行,加数为第1行,和数为第2行。
从个位开始,逐列逐行深搜。dfs参数有3个,分别为列号x,行号y,进位数t。当所有行dfs完后检验。这个解法得90分,卡掉一个点。
3、对2进行修改,不再dfs完所有行后检验,而是引入check()函数,若n个字符都已经填好数,则判断是否合法。这个解法较2有改进,dfs层数减少了,但还是卡掉一个点。
4、怎样再优化?以具体例子说明。
ABC
+DAB
=DDA
深搜试探:C=1,B=2,A=3,D=5,到此,第0、1、2列的字符都确定好,但第2列并不成立。
因此再引入CHECK()函数,从第0列到第n-1列检查,如果某列上的字符都确定了,但不成立,则回溯。
bool CHECK(){
int A,B,C;
for(int i=0;i<n;i++){
A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
if( A==-1 || B==-1 || C==-1 )continue;
if( (A+B)%n!=C && (A+B+1)%n!=C ) return 1;
}
return 0;
}
AC代码:
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
int n;
char s[3][30];
int a[3][30],vis[30],ans[30];
int flag=0;
bool CHECK(){
int A,B,C;
for(int i=0;i<n;i++){
A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
if( A==-1 || B==-1 || C==-1 )continue;
if( (A+B)%n!=C && (A+B+1)%n!=C ) return 1;
}
return 0;
}
int check1() {
for(int i=0;i<n;i++)
if(vis[i]==0)return 0;
return 1;
}
bool check2(){
int A,B,C,x=0;
for(int i=0;i<n-1;i++){
A=ans[ a[0][i] ];B=ans[ a[1][i] ];C=ans[ a[2][i] ];
if( (A+B+x)%n!=C )return 0;
x=(A+B+x)/n;
}
return 1;
}
void print()
{
for(int i=0;i<n-1;i++)printf("%d ",ans[i]);
printf("%d\n",ans[n-1]);
flag=1;
}
void dfs(int row,int col,int x){/*行,列,进位*/
if(flag)return;
if( CHECK() )return;
if(check1()){
if(check2()){
print();
flag=1;
}
return;
}
int t=a[row][col];
if(row!=2){
if(ans[t]==-1){
for(int i=n-1;i>=0;i--)
if(!vis[i]){
vis[i]=1;ans[t]=i;
dfs(row+1,col,x);
vis[i]=0;ans[t]=-1;
}
}
else dfs(row+1,col,x);
}
else{
int A=ans[ a[0][col] ],B=ans[ a[1][col] ];
int tmp=A+B+x;
if(ans[t]==-1){
if(!vis[tmp%n]){
ans[t]=tmp%n;vis[ans[t]]=1;
dfs(0,col+1,tmp/n);
vis[ans[t]]=0;ans[t]=-1;
}
}
else {
if(tmp%n==ans[t])dfs(0,col+1,tmp/n);
else return;
}
}
}
int main(){
memset(ans,-1,sizeof(ans));
cin>>n;
cin>>s[0]>>s[1]>>s[2];
for(int i=0;i<n;i++){
a[0][i]=s[0][n-1-i]-'A';a[1][i]=s[1][n-1-i]-'A';a[2][i]=s[2][n-1-i]-'A';
}
dfs(0,0,0);
return 0;
}
总结:用到的3个优化策略,需要仔细体会。
1、数字从大往小尝试;
2、深搜层数最多n层,故所有字符填上数字后就检查整个等式是否成立。
3、前面几列填上数字后,检查到后面是否有某列不成立——这是关键优化。
另:本题还有一种解法,引入4个dfs函数,用于处理一列上有3个、2个、1个、0个字符未知共4种情况,但写起来很考验一个人的功力。以后有时间再给出这种解法。