[BZOJ1902]:[NOIP2004]虫食算(搜索)

题目传送门


题目描述

所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。

来看一个简单的例子:

  43#98650#45+8468#6633=44445506978

其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是53,第二行的数字是5。 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的。我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表中的前N个大写字母来表示这个算式中的0N-1N个不同的数字(但是这N个字母并不一定顺序地代表0N-1)。输入数据保证N个字母分别至少出现一次。

    BADC+CBDA=DCCC

上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。


输入格式

包含4行。第一行有一个正整数N,后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。


输出格式

包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。


样例

样例输入:

5
ABCED
BDACE
EBBAA

样例输出:

1 0 3 4 2


数据范围与提示

对于全部的数据,保证有N≤26


题解

官方正解是高斯消元,不得不承认高斯消元用来做这道题的确要比搜索简单。

先来算一下时间复杂度,高斯消元:O(${n}^{3}$),搜索:O(${n}^{n}$)

不管你怎么说,我感觉${n}^{n}$挺帅的。

所以我就去搜索了哇~

显然不能直接爆搜,卡长卡的好没准能跑过n=9n≥10想都别想了。

那么考虑剪枝:

为简便,地一个字符串称为A,第二个字符串称为B,第三个字符串称为C

首先你肯定会想到,如果A+B肯定不能得到C,那么就不要往下搜了。

这里需要注意的是,有可能会存在进位。

然后接着考虑,小学的时候数学老师教给你竖式加法的时候,我们都是从最右边网左算。

那么转换到这道题,我们也可以从右往左搜,这样就能更快的把枝剪掉了(注意你的judge函数要优秀)。

还有一种情况,就是如果最前面出现了进位,那么是要剪掉的,所以我们先搜大的数可以更快的出现进位,进而更快的剪掉这个枝。

所以从大往小搜必不可少。

有了这些我就可以轻松A掉这道题了,但是作为一名卓越的OIER,我们还要考虑怎么让代码跑的更快。

所以我就讲一下所有的剪枝:

1.最前面出现进位直接return false,或者AB串开头位相加比C串开头位大,也要return false

2.从左往右开始judge,首先,在A,B,C串都没有出现有字母没有赋值的情况的时候,可以按照严格的带进位的加法进行判断。

3.在出现了一个数没有赋值之后,我们就要按照考虑进位与不进位开始考虑了。

  $\alpha$.最好想到的就是无论进不进位都无法满足的话,就return false

  $\beta$.如果当前位有两个数已经赋值,一个数还没有赋值,那么如果根据这两个数所算出来的第三个数已经被使用(赋值不能重复),那么可以提前剪掉。

如果这些都能满足的话,则继续往前搜。


代码时刻

#include<bits/stdc++.h>
using namespace std;
bool vis[27];
int wzc[27];
int ans[27];
int n;
int a[27],b[27],c[27];
int sor[27],cnt;
char ch1[27],ch2[27],ch3[27];
inline bool _wzc()//judge
{
    int wzc=0,_=0;
    if(ans[a[0]]+ans[b[0]]>=n)return 0;//判断开头进不进位
    if(ans[c[0]]!=-1)//还是在开头剪枝
    {
        if(ans[a[0]]>ans[c[0]]||ans[b[0]]>ans[c[0]])return 0;
        if(ans[a[0]]!=-1&&ans[b[0]]!=-1&&ans[a[0]]+ans[b[0]]!=ans[c[0]]&&ans[a[0]]+ans[b[0]]+1!=ans[c[0]])return 0;
    }
    for(int i=n-1;i>=0;i--)//全都被赋过值
    {
        if(ans[a[i]]==-1||ans[b[i]]==-1||ans[c[i]]==-1)//出现有没有被赋过值的
        {
            _=i;
            break;
        }
        if((ans[a[i]]+ans[b[i]]+wzc)%n!=ans[c[i]])return 0;//严格带进未搜索
        wzc=(ans[a[i]]+ans[b[i]]+wzc)/n;
    }
    for(int i=_;i>=0;i--)//开始考虑进位与不进位,以下分情况,注意代码中“!=”和“==”。
    {
        if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
            if((ans[a[i]]+ans[b[i]])%n!=ans[c[i]]&&(ans[a[i]]+ans[b[i]]+1)%n!=ans[c[i]])return 0;
        if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]==-1)
            if((vis[(ans[a[i]]+ans[b[i]])%n])&&vis[(ans[a[i]]+ans[b[i]]+1)%n])return 0;
        if(ans[a[i]]!=-1&&ans[b[i]]==-1&&ans[c[i]]!=-1)
            if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
        if(ans[a[i]]==-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
            if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
    }
    return 1;
}
void dfs(int x)
{
    if(!_wzc())return;
    if(x==n)
    {
        if(_wzc())
        {
    		for(int i=0;i<n;i++)
    		    printf("%d ",ans[i]);
    		exit(0);//搜到结果直接结束
        }
    }
    for(int i=n-1;i>=0;i--)//反向搜
    {
        if(!vis[i])
        {
            vis[i]=1;
            ans[sor[x]]=i;
            dfs(x+1);
            vis[i]=0;
            ans[sor[x]]=-1;
        }
    }
    return;
}
int main()
{
    scanf("%d",&n);
    scanf("%s%s%s",ch1,ch2,ch3);
    for(int i=0;i<n;i++)
    {
        a[i]=ch1[i]-'A';
        b[i]=ch2[i]-'A';
        c[i]=ch3[i]-'A';
        ans[i]=-1;
    }
    for(int i=n-1;i>=0;i--)//统计搜索顺序
    {	
        if(!vis[a[i]])
        {	
            vis[a[i]]=1;
            sor[cnt++]=a[i];
        }
        if(!vis[b[i]])
        {
            vis[b[i]]=1;
            sor[cnt++]=b[i];
        }
        if(!vis[c[i]])
        {
            vis[c[i]]=1;
            sor[cnt++]=c[i];
        }
    }
    memset(vis,0,sizeof(vis));
    dfs(0);
    return 0;
}

rp++

posted @ 2019-07-05 12:22  HEOI-动动  阅读(210)  评论(0编辑  收藏  举报