虫食算题解

虫食算题目出处(3703)

此题的直接思路是搜索,但搜索得有技术(其实就是剪枝),不然会超时
膜拜自己
这道题从个位往最高位搜,就可以避免进位的重复运算。。。。。。
最后check一下,然后递归输出
先上代码,后面再来分段讨论

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
bool flag[95];
char a[30],b[30],c[30],d[30];
int v[95],n;
inline void ycl() {
    int tot=0;
    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));
}
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=65;i<=64+n;i++)
        if(v[i]!=-1)
            printf("%d ",v[i]);
    exit(0);
}
inline bool check() {
    for(int i=n-1;i>=0;i--)
        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]])
                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 void dfs(int i) {
    if(i>n) {
        if(ok())
            pr();
        return;
    }
    for(int j=n-1;j>=0;j--) {
        if(flag[j])
            continue;
        v[d[i]]=j;
        flag[j]=1;
        if(!check()) {
            v[d[i]]=-1;
            flag[j]=0;
            continue;
        }
        dfs(i+1);
        v[d[i]]=-1;
        flag[j]=0;
    }
}
int main() {
    scanf("%d\n",&n);
    gets(a);
    gets(b);
    gets(c);
    ycl();
    dfs(1);
}

主函数的代码很好懂,我们就直接看dfs:

inline void dfs(int i) {
    if(i>n) {
        if(ok())
            pr();
        return;
    }
    for(int j=n-1;j>=0;j--) {
        if(flag[j])
            continue;
        v[d[i]]=j;
        flag[j]=1;
        if(!check()) {
            v[d[i]]=-1;
            flag[j]=0;
            continue;
        }
        dfs(i+1);
        v[d[i]]=-1;
        flag[j]=0;
    }
}

再来看ok:

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;
}

然后是pr:


inline void pr() {
    for(int i=65;i<=64+n;i++)就是输出嘛,A对应的ASCLL码值为65
        if(v[i]!=-1)//不为-1就输出
            printf("%d ",v[i]);
    exit(0);//有答案了,直接结束整个程序
}

再来看最复杂的(汗)

check:

inline bool check() {
    for(int i=n-1;i>=0;i--)
        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]])//已经不成立了
                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;
}

预处理就不用我说了,大家还有不懂的可以一起讨论讨论(^ v ^)

猜你喜欢

转载自blog.csdn.net/qq_43890190/article/details/84676673
今日推荐