【搜索】【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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章