洛谷P1092 蟲食算(回溯法)

2021-08-09 02:50:18 字數 2446 閱讀 9083

所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看乙個簡單的例子:

其中#號代表被蟲子啃掉的數字。根據算式,我們很容易判斷:第一行的兩個數字分別是5和3,第二行的數字是5。

現在,我們對問題做兩個限制:

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

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

上面的算式是乙個4進製的算式。很顯然,我們只要讓abcd分別代表0123,便可以讓這個式子成立了。你的任務是,對於給定的n進製加法算式,求出n個不同的字母分別代表的數字,使得該加法算式成立。輸入資料保證有且僅有一組解

包含四行。第一行有乙個正整數n(n<=26),後面的3行每行有乙個由大寫字母組成的字串,分別代表兩個加數以及和。這3個字串左右兩端都沒有空格,從高位到低位,並且恰好有n位。

包含一行。在這一行中,應當包含唯一的那組解。解是這樣表示的:輸出n個數字,分別表示a,b,c……所代表的數字,相鄰的兩個數字用乙個空格隔開,不能有多餘的空格。

5abced

bdace

ebbaa

1 0 3 4 2

對於30%的資料,保證有n<=10;

對於50%的資料,保證有n<=15;

對於全部的資料,保證有n<=26。

noip2004提高組第4題

基本思路是用回溯法對每乙個字母進行列舉(0到n-1),但如不進行剪枝和優化,則肯定有測試用例超時(複雜度o(n!))。

題目分析:共有n個不同的字母,每個字母表示不同的數字(0到n-1);a, b, c的長度相同,因此最高位沒有進製。

可以進行如下剪枝和優化:

1、與手工計算加法的方法一樣,從個位數開始搜尋。為了方便起見,開始先將三個字串逆序。

2、對每乙個字母列舉的順序為從n-1到0,因為最高位沒有進製,這樣搜尋可以更快地得到結果。

3、在列舉字母時,按照從低位到高位第一次出現的字母的次序進行。這樣在回溯時可以更有效率地判斷是否進入下乙個分支。

4、在判斷可以進入下一分支前(假設已經判斷到a, b, c的第i個字元),如果在i到最高位之間的某個位置j,a[j],b[j], c[j]都已確定,則判斷它們是否合法,如果不合法,則不能進入下乙個分支。由於不知道進製,但進製只能是0或1,因此在判斷是否合法時,在(a[j]+b[j])%n和(a[j]+b[j]+1)%n這兩個數中,如果都不等於c[j],則肯定是不合法的。

5、在對乙個字母進行列舉時,如果其第一次出現的在i列,但在i列的其他兩個元素都已經知道,則可有由他兩個元素的值和進製計算該字母應該是哪個數字,而不需要從0~n逐個列舉。在判斷是否可行時,只要判斷從i開始向後的各列即可(因為能到i列,說明前面i-1列都合法),由於前i-1列都合法,在判斷前i-1列時可以得到每一步的進製數carry。要實現該優化,還必須記錄每乙個字母第一次出現的列數。

6、在判斷是否進入下一分支前,如果判斷到某列i中有2個字母已確定,則計算第三個字母(進製為carry[i-1])的值,如果該值已經被其他字母占用,則不能進入下一分支。

7、盡量避免採用除法,因為除法的計算時間比其他運算子都要大。

8、由於a, b, c的都是n位,因此,如果最高位進製,則不能進入下一分支。

9、如果得到結果,則盡快退出回溯,可採用flag判斷是否已得到結果。

10、盡量不要用stl中的容器,本來打算採用map儲存字母和值之間的對映關係,但測試用例9始終tle,改掉之後一下變為24ms,其他都為0ms。看來容器所增加的複雜度係數不容小覷。

#include 

#include

#include

#include

#include

using namespace std; int n; string a, b, c; int alpha[26], d[26], digit[26], cnt = 0, used[26] = , vis[26] = ; int order[26], order_i[26], flag = 0; int digit_map[26]; // 優化10 char digit_map1[26]; // 優化10 char digit_c[26]; string reverse(string str) else if(vis[i_a] && !vis[i_b] && vis[i_c]) if(!(vis[i_a] && vis[i_b] && vis[i_c])) p = carry1 + digit[i_a] + digit[i_b]; // 優化7 if(p>=n)else if(p1!=digit[i_c]) return 0; i++; } if(carry1) // 優化8 return 0; if(i==n){ for(i=0; i

洛谷P1092 蟲食算

所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看乙個簡單的例子 其中 號代表被蟲子啃掉的數字。根據算式,我們很容易判斷 第一行的兩個數字分別是5和3,第二行的數字是5。現在,我們對問題做兩個限制 首先,我們只考慮加法的蟲食算。這裡的加法是n進製加法,算...

洛谷P1092 蟲食算

所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看乙個簡單的例子 其中 號代表被蟲子啃掉的數字。根據算式,我們很容易判斷 第一行的兩個數字分別是5和3,第二行的數字是5。現在,我們對問題做兩個限制 首先,我們只考慮加法的蟲食算。這裡的加法是n進製加法,算...

洛谷 P1092 蟲食算

所謂蟲食算,就是原先的算式中有一部分被蟲子啃掉了,需要我們根據剩下的數字來判定被啃掉的字母。來看乙個簡單的例子 其中 號代表被蟲子啃掉的數字。根據算式,我們很容易判斷 第一行的兩個數字分別是5和3,第二行的數字是5。現在,我們對問題做兩個限制 首先,我們只考慮加法的蟲食算。這裡的加法是n進製加法,算...