ACWing184蟲食算題解

題目傳送門:https://www.acwing.com/problem/content/186/
【題目大意】給定一個字母組成的加法豎式,蟲子把所有的數都啃光了,我們只知道哪些數字是相同的,我們將相同的數字用相同的字母表示,不同的數字用不同的字母表示。如果這個算式是N進制的,我們就取英文字母表的前N個大寫字母來表示這個算式中的0到N-1這N個不同的數字:但是這N個字母並不一定順序地代表0到N-1。輸入數據保證N個字母分別至少出現一次。
   BADC
+  CBDA
------
  DCCC

上面的算式是一個4進制的算式。
很顯然,我們只要讓ABCD分別代表0123,便可以讓這個式子成立了。你的任務是,對於給定的N進制加法算式,求出N個不同的字母分別代表的數字,使得該加法算式成立。輸入數據保證有且僅有一組解。
【輸入格式】
輸入包含4行。
第一行有一個正整數N(N<=26),後面的3行每行有一個由大寫字母組成的字符串,分別代表兩個加數以及和。
這3個字符串左右兩端都沒有空格,並且恰好有N位。
【輸出格式】
輸出包含一行。
在這一行中,應當包含唯一的那組解。
解是這樣表示的:輸出N個數字,分別表示A,B,C……所代表的數字,相鄰的兩個數字用一個空格隔開,不能有多餘的空格。
【輸入樣例】:
5
ABCED
BDACE
EBBAA
【輸出樣例】:
1 0 3 4 2
  分析:起初看到這題,我想不就是需要知道A,B,C…分別一 一對應0,1,2,…n-1之間哪個數麼,全排列後代入豎式判斷其是否是成立的就可以了。但是看到N的規模,知道列舉全排列的時間複雜度就會到大N!那可是個很客觀的數字。
  這樣看來是不可以的,如果等到全排列再來判斷時間上承受不了,那麼換個方式搜索吧,要想搜索速度快,就需要及時剪掉不可行的解,我們可以按照加法的先後順序去一個個搜索每個位置上的可能的值。也就是說,從豎式的右向左,從上到下的先後位置搜索。在搜的過程中,根據已經搜來的路上確定的一些字母,對還未確定的高位進行預測.
  搜索時需要記錄搜到了豎式中的哪個位置(row,col),和到當前位置是否有進位jw。算法僞代碼爲:
  1.如果已經有方案了,直接退出。
  2.如果3*n個位置已搜完了,如果高位有進位,則不合法,否則輸出解。
  3.預測已經產生的字母對應的數對當前搜索的列之前的高位列的合法性,進行可行性剪枝。具體方法時從當前列枚舉到高位列,只要着一列的上中下三行上x,y,z都有對應的數,則根據加法進位只有0和1的可能性這個特徵,即(x+y)%n == z 或者(x+y+1) %n== z來判斷是否之前的搜索有繼續搜索下去的必要性。
  4.如果搜到的當前位置上的字母沒有對應值,則從大到小枚舉沒有用過的值,如果是第一行和第二行的位置上需要這個值時,直接用上,並做標記,繼續搜索本列下一行的位置上的值;如果是第三行的位置上需要這個值,則需要看看這個值是否是上面第一行+第二行+進位jw的和,如果不是,這個數還不能用,否則則將這個數用上,並轉去搜索下一列第一行的值。
   如果搜到的位置上的字母已經在低位搜索時已經有指定的值,如果是第一和第二行,則直接轉去搜索同列下一行的位置上的數;如果是第三行,則先判斷改位置上的值是否等於第一行+第二行+進位jw的和,如果不是,則說明此搜索不可能,如果是,則計算進位轉去下一列的第一行去繼續搜索。
   關於字母與數字的對應可以用map,也可以用數組映射,數組的方式在ACWing上AC,map的方式則TLE。
  代碼如下:

#include<bits/stdc++.h>
using namespace std;
int n;
string s[4];
//map<char,int> mp;
int mp[30];
int vis[27];
bool flag =  false;
void dfs(int row,int col, int jw){
	if(flag == true) return;
	if(col == -1){//所有列都已處理完 
		if(jw != 0) return;//高位不允許有進位。
		for(int i = 0;i< n; i++){
			printf("%d ",mp[i] );
		} 
		flag = true;
		return ;
	}
	//針對已經映射了的字母對應的數字,預見col-1 ->0高位部分是否可行
	for(int i = col-1; i>=0; i--){
		int x = mp[s[1][i]-'A'], y = mp[s[2][i]-'A'],z = mp[s[3][i]-'A'];
		if(x ==-1 || y == -1 || z == -1) continue;
		//對於已經產生了的數分析合法性,對於上面加下面加上可能的進位1 or 0後均不等於z的果斷剪枝。 
		if((x+y) % n != z && (x+ y + 1)%n != z) return; 
	} 
	if(mp[s[row][col]-'A'] == -1){//此位置上的字母沒有確定時 
		for(int i = n-1; i >= 0; i--){
			if(!vis[i]){
				if(row != 3){//如果實在加數上
					mp[s[row][col]-'A'] = i;
					vis[i] = 1;
					dfs(row+1 , col, jw);
					vis[i] = 0;
					mp[s[row][col]-'A'] = -1; 					
				}
				else{//如果已經搜索到了col列的第三行 
					int t = mp[s[1][col]-'A'] + mp[s[2][col]-'A'] + jw;
					if(t % n != i) continue;//如果i不是col列倆加數的和的餘數,則i不是存放至此的數。
					vis[i] = 1;
					mp[s[row][col]-'A'] = i;
					dfs(1, col - 1, t/n);
					vis[i] = 0;
					mp[s[row][col]-'A'] = -1;  
				}
			}
		}
	}
	else{//此位置上字母已確定時. 
		if(row != 3){//直接轉去搜索下一行。 
			dfs(row +1,col,jw);
		}
		else{
			int t = mp[s[1][col]-'A'] + mp[s[2][col]-'A'] + jw;
			if(t % n != mp[s[3][col]-'A']) return;
			dfs(1, col - 1, t/n);
		}
	}
}
int main(){
    cin >> n;
    cin >> s[1] >> s[2] >> s[3];
    //初始化每個字母對應的數爲-1。 
    for(int i = 0; i< n; i++)
    	mp[i] = -1;
    dfs(1 , n-1 , 0) ;//從第一行最低位開始搜索,此時進位爲0; 
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章