洛谷P1092 虫食算——DFS,优化

题目: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种情况,但写起来很考验一个人的功力。以后有时间再给出这种解法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值