所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。
来看一个简单的例子:
43#9865#045
+ 8468#6633
--------------
44445509678
其中 #
号代表被虫子啃掉的数字。
根据算式,我们很容易判断:第一行的两个数字分别是 55 和 33,第二行的数字是 55。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。这里的加法是 N� 进制加法,算式中三个数都有 N� 位,允许有前导的 00。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。
如果这个算式是 N� 进制的,我们就取英文字母表的前 N� 个大写字母来表示这个算式中的 00 到 N−1�−1 这 N� 个不同的数字:但是这 N� 个字母并不一定顺序地代表 00 到 N−1�−1。
输入数据保证 N� 个字母分别至少出现一次。
BADC
+ CBDA
----------
DCCC
上面的算式是一个 44 进制的算式。
很显然,我们只要让 ABCD���� 分别代表 01230123,便可以让这个式子成立了。
你的任务是,对于给定的 N� 进制加法算式,求出 N� 个不同的字母分别代表的数字,使得该加法算式成立。
输入数据保证有且仅有一组解。
代码:
#include <bits/stdc++.h>
using namespace std;
#define fir(i,a,b) for(int i=a;i<=b;i++)
const int N=27;
int n,f[N],vis[N],ans[N];
char a[30],b[30],c[30],d[30];
void init()
{
fir(i,1,n)//将字符变成整数
{
a[i]-='A';
b[i]-='A';
c[i]-='A';
}
int cnt=0;
for (int i=n; i; i--)
{
if (!vis[a[i]])//如果这个数还没有归纳进来的话,下面同理
{
d[cnt++]=a[i];
vis[a[i]]=1;
}
if (!vis[b[i]])
{
d[cnt++]=b[i];
vis[b[i]]=1;
}
if (!vis[c[i]])
{
d[cnt++]=c[i];
vis[c[i]]=1;
}
}
}
bool check()
{
int p = 0;
for (int i=n; i>=1; i--)
{
if (f[a[i]]==-1 || f[b[i]]==-1 || f[c[i]]==-1)//如果有一个数字没确定
p=-1;
else
{
if (p==-1)
{
if ((f[a[i]]+f[b[i]])%n==f[c[i]])
p=(f[a[i]]+f[b[i]])/n;
else if ((f[a[i]]+f[b[i]]+1)%n==f[c[i]])
p=(f[a[i]]+f[b[i]]+1)/n;
else
return 1;
}
else
{
if ((f[a[i]]+f[b[i]]+p)%n==f[c[i]])
p=(f[a[i]]+f[b[i]]+p)/n;
else
return 1;
}
}
}
return p == 1? true:false;
}
bool dfs(int x)
{
if (x == n)
{
memcpy(ans, f, sizeof(f));//拷贝函数,具体可以百度.
return true;
}
for (int i = n - 1; i >= 0; i--)//从后到前枚举
{
f[d[x]]=i;
if (vis[i] || check())
continue;
vis[i]=1;
if (dfs(x + 1))
return true;
vis[i]=0;
}
f[d[x]]=-1;
return false;
}
int main()
{
scanf("%d\n%s %s %s",&n,a+1,b+1,c+1);//从第一位开始枚举
init();
memset(vis, 0, sizeof(vis));
memset(f, -1, sizeof(f));//初始化
dfs(0);
fir(i,0,n-1)
printf("%d ", ans[i]);//输出答案
return 0;
}