【搜索】【NOIP2004】虫食算

[NOIP2004]虫食算
时间限制: 1 Sec 内存限制: 64 MB

题目描述
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:
    43#9865#045
+     8468#6633
———————————     
44445506978
其中#号代表被虫子啃掉的数字。

根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。

现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。

如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。

输入数据保证N个字母分别至少出现一次。    

    BADC
+ CBDA
—————-   
    DCCC

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

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

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

样例输入
5
ABCED
BDACE
EBBAA
样例输出
1 0 3 4 2

【数据规模】
30N10
50N15
N26

看到这道题,先把暴力打出来。
就是枚举每一个字母所对应的数字,然后在枚举完后check一下就行了。
然后想剪枝,
(1)我们不妨从低位字母向高位字母枚举,如果有从第一位开始连续的 几列等式字母都枚举过,我们不妨模拟竖式计算,若发现矛盾 (A[i]+B[i]+) mod NC[i] ,就剪枝。
(2)对于某一列上只缺一个数的情况,我们可以根据另外两个数求出所缺数的近似值。
已知A[i],B[i]: C[i]=(A[i]+B[i])% N (A[i]+B[i]+1)%N
已知A[i],C[i]: B[i]=(C[i]A[i]+N)% N (C[i]A[i]1+N)%N
已知B[i],C[i]: C[i]=(C[i]B[i]+N)% N (C[i]B[i]1+N)%N

如果求出来的近似值都出现过,那么便剪掉。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

#define MAXN 26
#define MAXM
#define INF 0x3f3f3f3f
typedef long long int LL;

char A[MAXN+5],B[MAXN+5],C[MAXN+5];
char words[MAXN+5];int cnt;
char keys[MAXN+5];
bool used[MAXN+5];
int N;

void print()
{
    for(int i=0;i<N-1;++i)
        printf("%d ",keys[i]);
    printf("%d\n",keys[N-1]);

    return;
}

void change(char *str,char f,char t)
{
    for(int i=1;i<=N;++i)
        str[i]=(str[i]==f?t:str[i]);
}

bool check1()
{
    int res=0;
    for(int i=N;i>=1;--i)
    {
        if(A[i]>=N||B[i]>=N||C[i]>=N)
            return true;

        res=A[i]+B[i]+res;
        if(res%N!=C[i])
            return false;

        res/=N;
    }

    return true;
}

bool check2()
{
    int p,p1,p2;

    for(int i=N;i>=1;--i)//a,b,c
    {
        if(A[i]>=N||B[i]>=N||C[i]>=N)
            continue;

        p=(A[i]+B[i])%N;

        if(!(p==C[i]||(p+1)%N==C[i]))
            return false;
    }

    for(int i=N;i>=1;--i)//a,b,?
    {
        if(A[i]>=N||B[i]>=N||C[i]<N)
            continue;

        p1=(A[i]+B[i])%N;
        p2=(p1+1)%N;

        if(used[p1]&&used[p2])
            return false;
    }

    for(int i=N;i>=1;--i)//a,?,c
    {
        if(A[i]>=N||B[i]<N||C[i]>=N)
            continue;

        p1=(C[i]-A[i]+N)%N;
        p2=(p1-1)%N;

        if(used[p1]&&used[p2])
            return false;
    }

    for(int i=N;i>=1;--i)//?,b,c
    {
        if(A[i]<N||B[i]>=N||C[i]>=N)
            continue;

        p1=(C[i]-B[i]+N)%N;
        p2=(p1-1)%N;

        if(used[p1]&&used[p2])
            return false;
    }

    return true;
}

bool dfs(int now)
{
    if(!check1()||!check2())
        return false;

    if(now>cnt)
    {
        print();
        return true;
    }

    for(int i=N-1;i>=0;--i)
        if(!used[i])
        {
            used[i]=1;
            keys[words[now]-'A']=i;

            change(A,words[now],i);
            change(B,words[now],i);
            change(C,words[now],i);

            if(dfs(now+1))
                return true;

            change(A,i,words[now]);
            change(B,i,words[now]);
            change(C,i,words[now]);

            used[i]=0;
        }

    return false;
}

int main()
{
    scanf("%d",&N);
    scanf("%s%s%s",A+1,B+1,C+1);

    cnt=0;
    for(int i=N;i>=1;--i)
    {
        if(!used[A[i]-'A'])words[++cnt]=A[i],used[A[i]-'A']=1;
        if(!used[B[i]-'A'])words[++cnt]=B[i],used[B[i]-'A']=1;
        if(!used[C[i]-'A'])words[++cnt]=C[i],used[C[i]-'A']=1;
    }

    memset(used,0,sizeof(used));
    dfs(1);
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值