P1092 虫食算

https://www.luogu.org/problem/show?pid=1092

好一道搜索题!
一开始我自己写了一个史上最无脑的搜索,结果只得了10分,全部TLE掉。参考了题解,才发现自己的暴力搜方法不太对,我是枚举数的每一位是每一个数。。。就是好无脑,不超时才怪。

参考了题解(有些题解有太多太多的剪枝,好像处理得太过繁琐,我参考的题解简单易懂),叙述一下法。
我们可以这样来搜索:我们记录每个字母在三个数从低位到高位的出现顺序,按照这个顺序搜索当前的字母代表哪个数。
其中又一些小小的剪枝:

1).当我们三个数这一位上的字母都赋值后,如果a和b这一位上的数之和或者之和加上进位(假设有进位只能 为1)都不等于c上的数时,那么不能符合条件,return

2).如果搜索出最高位能够进位,那么也不可以

还有一个优化,给字母赋数值时,从大往小赋值,以为(最)高位的数不能太大。(最高位不能进位)
这样就可以AC啦

先贴一波原创的无脑搜索TLE的10分代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm> 
#include<cmath>
#include<map>
using namespace std;
int n;
int a[30],b[30],c[30];
int aa[30],bb[30],cc[30],dd[30];
bool f[30];
int p[30];
bool judge()
{
    for(int i=0;i<n;i++)
    {
        aa[i]=p[a[n-1-i]];
        bb[i]=p[b[n-1-i]];
        cc[i]=p[c[n-1-i]];
    }
    for(int i=0;i<n;i++) dd[i]=aa[i]+bb[i];
    for(int i=0;i<n;i++) dd[i+1]+=dd[i]/n,dd[i]%=n;
    for(int i=0;i<n;i++) if(dd[i]!=cc[i]) return 0;
    return 1;
}
bool check()
{
    int w=0;
    for(int i=0;i<n;i++)
    {
        if(!p[i]) w++;
        if(w>1) return 0;
    }    
    return 1;
}
void dfs()
{
    if(check()) 
    {
        if(judge())
        {
             for(int i=0;i<n;i++)
            printf("%d ",p[i]);
            exit(0);
        }
        else return; 
    }

    for(int x=n-1;x>=0;x--)
    {
        if(!f[x])
        {
            for(int i=n-1;i>=0;i--){
            if(!p[a[i]])
            {
                p[a[i]]=x,f[x]=1;
                dfs();
                p[a[i]]=0,f[x]=0;
            }
            if(!p[b[i]])
            {
                p[b[i]]=x,f[x]=1;
                dfs();
                p[b[i]]=0,f[x]=0;
            }
            if(!p[c[i]])
            {
                p[c[i]]=x,f[x]=1;
                dfs();
                p[c[i]]=0,f[x]=0;
            }
        }
      }

    } 
}
int main()
{
    scanf("%d",&n);
    char C;
    for(int i=0;i<n;i++) {cin>>C;a[i]=C-'A';}
    for(int i=0;i<n;i++) {cin>>C;b[i]=C-'A';}
    for(int i=0;i<n;i++) {cin>>C;c[i]=C-'A';} 

    dfs();
    return 0;
}

下面是AC代码

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm> 
#include<cmath>
#include<map>
using namespace std;
int n,cnt;
int a[30],b[30],c[30];
int aa[30],bb[30],cc[30];
bool f[30];//表示i这个数是否被用过 
int num[30];//num[i]表示i这个字母表示的数 
int nxt[30];
void show()//输出 
{
    for(int i=0;i<n;i++)
     printf("%d ",num[i]);
    exit(0);
}
bool judge()
{
    int last=0;//进位 
    for(int i=n-1;i>=0;i--)
    {
        if((num[a[i]]+num[b[i]]+last)%n!=num[c[i]]) return 0;
        last=(num[a[i]]+num[b[i]]+last)/n;
    }
    return 1;
}
bool check()
{
    for (int i=n-1;i>=0;i--)
     if((num[a[i]]!=-1)&&(num[b[i]]!=-1)&&(num[c[i]]!=-1))
        if((((num[a[i]]+num[b[i]])%n)!=num[c[i]])&&(((num[a[i]]+num[b[i]]+1)%n)!=num[c[i]])) 
           return 0;
    return 1;
}
void dfs(int x)//搜索第x个字母是哪个数 
{
    if(num[a[0]]+num[b[0]]>=n) return;

    if(!check()) return;

    if(x==n) {if(judge()) show();}
    for(int i=n-1;i>=0;i--)
    if(!f[i])
    {
        num[nxt[x]]=i;
        f[i]=1;
        dfs(x+1);
        f[i]=0;
        num[nxt[x]]=-1; 
    } 
}
int main()
{
    scanf("%d",&n);
    char C;
    for(int i=0;i<n;i++) {cin>>C;a[i]=C-'A';}
    for(int i=0;i<n;i++) {cin>>C;b[i]=C-'A';}
    for(int i=0;i<n;i++) {cin>>C;c[i]=C-'A';} 
    for(int i=n-1;i>=0;i--)
    {
        if(!f[a[i]]) nxt[cnt++]=a[i],f[a[i]]=1;
        if(!f[b[i]]) nxt[cnt++]=b[i],f[b[i]]=1;
        if(!f[c[i]]) nxt[cnt++]=c[i],f[c[i]]=1;
        if(cnt==n) break;//记录搜索的字母顺序 
    }
    //for(int i=0;i<n;i++) printf("%d ",nxt[i]);
    memset(f,0,sizeof(f));
    memset(num,-1,sizeof(num));//因为真正的数值有0,所以一开始初始化为-1 
    dfs(0); 
    return 0;
}
原文地址:https://www.cnblogs.com/dfsac/p/7587860.html