星星之火OIer:虫食算

题目链接

退役题解

同时致敬我的第一篇题解

这是一道搜索题

依次枚举每一个字母的值

然后要从低位开始搜

提前算进位

更多解释看代码

#include
#include
#include
#include
using namespace std;
bool flag[95];
char a[28],b[28],c[28],d[28];
int v[95],n,tot;
inline void ycl() {//预处理,从低位到高位统计,以免重复计算进位
    for(int i=n-1;i>=0;i--) {
        if(!flag[a[i]]) {//每一个字母只统计一次
            flag[a[i]]=1;
            d[++tot]=a[i];//最先的肯定是最低位
        }
        if(!flag[b[i]]) {
            flag[b[i]]=1;
            d[++tot]=b[i];
        }
        if(!flag[c[i]]) {
            flag[c[i]]=1;
            d[++tot]=c[i];
        }
    }
    memset(flag,0,sizeof(flag));
    memset(v,-1,sizeof(v));//一定有一个字母为0,清为-1
}
inline bool check() {
    for(int i=n-1;i>=0;i--) {//从低位到高位每一位都要check
        if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]!=-1) {//三个数字都知道
            if((v[a[i]]+v[b[i]])%n!=v[c[i]]&&(v[a[i]]+v[b[i]]+1)%n!=v[c[i]])//a[i]+b[i]正好是c[i]或进了一位
                return 0;
        }
        else if(v[a[i]]!=-1&&v[b[i]]!=-1&&v[c[i]]==-1) {//下面三个都是确定两个之后剩下的一个就只有两种可能(正好或进(退)位)
            if(flag[(v[a[i]]+v[b[i]])%n]&&flag[(v[a[i]]+v[b[i]]+1)%n])//如果这个两个可能的数都被用过,肯定不是正解
                return 0;
        }
        else if(v[a[i]]!=-1&&v[b[i]]==-1&&v[c[i]]!=-1) {
            if(flag[(v[c[i]]-v[a[i]]+n)%n]&&flag[(v[c[i]]-v[a[i]]-1+n)%n])
                return 0;
        }
        else if(v[a[i]]==-1&&v[b[i]]!=-1&&v[c[i]]!=-1) {
            if(flag[(v[c[i]]-v[b[i]]+n)%n]&&flag[(v[c[i]]-v[b[i]]-1+n)%n])
                return 0;
        }
    }
    return 1;
}
inline bool ok() {//从低位到高位依次比对
    int jw=0;//进位
    for(int i=n-1;i>=0;i--) {
        if((v[a[i]]+v[b[i]]+jw)%n!=v[c[i]])//结果不等
            return 0;//肯定不对
        jw=(v[a[i]]+v[b[i]]+jw)/n;//计算有没有进位
    }
    if(jw)//如果计算完了仍然有进位,肯定是错的
        return 0;
    return 1;
}
inline void pr() {
    for(int i='A';i<='A'-1+n;i++)//v[i]就是i这个字母所代表的值
        if(v[i]!=-1)
            printf("%d ",v[i]);
    exit(0);//直接结束全部程序
}
void dfs(int x) {
    if(x>n) {//所有位数都已确定
        if(ok()) {//计算正确
            pr();//输出答案
        }
        return;//不管怎样都要返回
    }
    for(int i=n-1;i>=0;i--) {
        if(flag[i])//i这个值被用过
            continue;
        v[d[x]]=i;//将字母d[x]的值设定为i
        flag[i]=1;//i已被用过
        if(!check()) {//已经不成立
            v[d[x]]=-1;
            flag[i]=0;
            continue;//回溯
        }
        dfs(x+1);//继续搜
        v[d[x]]=-1;//回溯
        flag[i]=0;
    }
}
int main() {
    scanf("%d%s%s%s",&n,a,b,c);
    ycl();
    dfs(1);
}

 

你可能感兴趣的:(信息学奥赛专栏,搜索)