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;
}

发布了88 篇原创文章 · 获赞 22 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/xuechen_gemgirl/article/details/90516566
今日推荐