【搜索】$P1092$蟲食算

【搜索】\(P1092\)蟲食算

題目鏈接

首先,我們只考慮加法的蟲食算。這裏的加法是N進制加法,算式中三個數都有N位,允許有前導的0。

其次,蟲子把所有的數都啃光了,我們只知道哪些數字是相同的,我們將相同的數字用相同的字母表示,不同的數字用不同的字母表示。如果這個算式是N進制的,我們就取英文字母表午的前N個大寫字母來表示這個算式中的0到N-1這N個不同的數字:但是這N個字母並不一定順序地代表0到N-1。輸入數據保證N個字母分別至少出現一次。輸入數據保證有且僅有一組解。

輸入格式

包含四行。
第一行有一個正整數\(N(N \leq 26)\)

後面的三行,每行有一個由大寫字母組成的字符串,分別代表兩個加數以及和。這3個字符串左右兩端都沒有空格,從高位到低位,並且恰好有N位。

輸出格式

一行,即唯一的那組解。

解是這樣表示的:輸出N個數字,分別表示A,B,C,…所代表的數字,相鄰的兩個數字用一個空格隔開,不能有多餘的空格。

對於30%的數據,保證有\(N \leq 10\)

對於50%的數據,保證有\(N \leq 15\)

對於全部的數據,保證有\(N \leq 26\)

Solution

搜索是比較好想的。但若直接枚舉全排列需要n!,那麼需要剪枝。

考慮加法,進位的話只會進1,所以如果(A + B) % n != C && (A + B +1) % n != C,顯然不合法。然後因爲三個數都是N位,所以最高位不可能進位。

那麼什麼搜索順序會更優呢?從右往左,也就是從低位到高位。從右往左,按照字母出現順序搜索,能在很大程度上提高剪枝效率。

最後,最關鍵的一點,是一個exit(0)的應用。第一次知道這到底是個什麼。

exit(0):正常運行程序並退出程序。 exit 是一個函數。 exit是系統調用級別的,它表示了一個進程的結束。 exit是進程的退出。 exit是操作系統提供的(或者函數庫中給出的)。 exit函數是退出應用程序,刪除進程使用的內存空間,並將應用程序的一個狀態返回給OS,這個狀態標識了應用程序的一些運行信息,這個信息和機器和操作系統有關,一般是 0 爲正常退出,非0 爲非正常退出。 6. 非主函數中調用return和exit效果很明顯,但是在main函數中調用return和exit的現象就很模糊,多數情況下現象都是一致的。

#include <algorithm>
#include <iostream>
#include <cstdio>
using namespace std;
long long read(){
  long long x = 0; int f = 0; char c = getchar();
  while(c < '0' || c > '9') f |= c == '-', c = getchar();
  while(c >= '0' && c <= '9') x = (x << 3) + (x << 1) + (c ^ 48), c = getchar();
  return f? -x:x;
}

int a[30], b[30], c[30], num[30], qu[30], n, cnt;
char s1[30], s2[30], s3[30];
bool used[30];
bool ok(){//最後判斷一下是否滿足等式
    for(int i = n - 1, x = 0; i >= 0; --i){
        int A = num[a[i]], B = num[b[i]], C = num[c[i]];
        if((A + B + x) % n != C) return 0;
        x = (A + B + x) / n;
    }
    return 1;
}
void print(){//輸出
    printf("%d", num[0]);
    for(int i = 1; i < n; ++i) printf(" %d", num[i]);
    exit(0);//減少遞歸返回時間
}
void dfs(int x){
    if(num[a[0]] + num[b[0]] >= n) return;//最高位沒有進位
    for(int i = n - 1; i >= 0; --i){
        int A = num[a[i]], B = num[b[i]], C = num[c[i]];
        if(A == -1 || B == -1 || C == -1) continue;
        if((A + B) % n != C && (A + B + 1) % n != C) return;//判每一位是否合法
    }
    if(x == n){
        if(ok()) print();
        return;
    }
    for(int i = n - 1; i >= 0; --i)
        if(!used[i]){
            num[qu[x]] = i, used[i] = 1;
            dfs(x + 1);
            num[qu[x]] = -1, used[i] = 0;//回溯
        }
}
void Sort(int x){//預處理遞歸枚舉順序
    if(!used[x]) used[x] = 1, qu[cnt++] = x;
}
int main(){
    freopen("1.txt", "r", stdin);
    freopen("1.out", "w", stdout);
    n = read();
    scanf("%s%s%s", s1, s2, s3);
    for(int i = 0; i < n; ++i)
        a[i] = s1[i] - 'A', b[i] = s2[i] - 'A', c[i] = s3[i] - 'A', num[i] = -1;
    for(int i = n - 1; i >= 0; --i){
        Sort(a[i]);//預處理順序
        Sort(b[i]);
        Sort(c[i]);
    }
    for(int i = 0; i < n; ++i) used[i] = 0;
    dfs(0);
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章