P1092 虫食算

来源:互联网 发布:imap发件箱端口区别 编辑:程序博客网 时间:2024/06/10 00:00

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;}
原创粉丝点击