NOIP2004虫食算

NOIP2004提高组 虫食算

题目描述 Description

 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。来看一个简单的例子:

       43#9865#045
    +    8468#6633
       44445506978

    其中#号代表被虫子啃掉的数字。根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5。

    现在,我们对问题做两个限制:

    首先,我们只考虑加法的虫食算。这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。

    其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:但是这N个字母并不一定顺序地代表0到N-1)。输入数据保证N个字母分别至少出现一次。



            BADC
      +    CBDA
            DCCC

    上面的算式是一个4进制的算式。很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解,

输入描述 Input Description

输入包含4行。第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。

输出描述 Output Description

  输出包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。

样例输入 Sample Input

5
ABCED
BDACE
EBBAA

样例输出 Sample Output

1 0 3 4 2

数据范围及提示 Data Size & Hint

对于30%的数据,保证有N<=10;
对于50%的数据,保证有N<=15;
对于全部的数据,保证有N<=26。

     历经无数次的失败后,我终于把这道水题A掉了,接下来讲讲心得

    这是一道令人发指的搜索题,

    首先,很容易想到的就是从低位到高位搜索,这个我也一开始也想到了,

    其次,就是剪枝了。很容易想到的是,如果当前位上三个数都已知但a+b+进位≠c,要剪枝,但是事实证明,这样只能得60分,于是乎,我就卡在这里了,我后来想过好多好多剪枝,但不是因为太麻烦就是因为效果差而放弃了,事实上,我已经离成功很近了

    能够想到对当前位上等式的判断,为何就想不到对所有位上都做一次这样的判断呢,于是就产生了check()函数,每次进入dfs(),都进行一次check()操作:

    枚举所有的位,对于某一位,若a、b、c都已知,且有((a+b)%N≠c and (a+b+1)%N≠c),则当前状态需要剪枝

    于是,就愉快的AC了

    实际上,还有一个剪枝(比较复杂),但是这样已经能AC了,就不费那个功夫了

代码:

//NOIP2004 虫食算 搜索
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define maxn 50
#define unknow -1

using namespace std;

char A[maxn], B[maxn], C[maxn], sum, **hash[5];
int num[500], N, shi[5][maxn];
bool used[maxn];

void show()
{
	int i;
	printf("%d",num['A']);
	for(i='B';i<'A'+N;i++)
		printf(" %d",num[i]);
	printf("\n");
	exit(0);
}

bool check()
{
	int i, j, a, b, c, s;
	for(i=1;i<=N;i++)
	{
		a=shi[1][i];
		b=shi[2][i];
		c=shi[3][i];
		if(a==unknow || b==unknow || c==unknow)
			continue;
		s=(a+b)%N;
		if(s!=c && (s+1)%N!=c)return false;
	}
	return true;
}

void change(const char c, const int k)
{
	int i;
	for(i=1;i<=N;i++)if(A[i]==c)shi[1][i]=k;
	for(i=1;i<=N;i++)if(B[i]==c)shi[2][i]=k;
	for(i=1;i<=N;i++)if(C[i]==c)shi[3][i]=k;
}

void dfs(int d,int jin)
{
	if(d==0 && jin==0)show();
	if(!check())return;
	int i, j, k, t, fa, fb, fc;
	fa=(num[A[d]]==unknow);
	for(i=0;i<N;i++)
	{
		if(fa)
			if(!used[i])
			{
				num[ A[d] ]=i;
				used[i]=true;
				change(A[d],i);
			}
			else continue;
		fb=(num[ B[d] ]==unknow);
		for(j=0;j<N;j++)
		{
			if(fb)
				if(!used[j])
				{
					num[ B[d] ]=j;
					used[j]=true;
					change(B[d],j);
				}
				else continue;
			fc=(num[ C[d] ]==unknow);
			t=shi[1][d]+shi[2][d]+jin;
			k=t/N;
			t%=N;
			if(fc)
			{
				if(!used[t])
				{
					num[ C[d] ]=t;
					used[t]=true;
					change(C[d],t);
					dfs(d-1,k);
					num[ C[d] ]=unknow;
					used[t]=false;
					change(C[d],unknow);
				}
			}
			else
			{
				if(num[ C[d] ]==t)
				dfs(d-1,k);
			}
			if(fb)
			{
				num[ B[d] ]=unknow;
				used[j]=false;
				change(B[d],unknow);
			}
			else break;
		}
		if(fa)
		{
			num[ A[d] ]=unknow;
			used[i]=false;
			change(A[d],unknow);
		}
		else break;
	}
}

int main()
{
	char s[50], i, j;
	memset(num,unknow,sizeof(num));
	memset(shi,unknow,sizeof(shi));
	scanf("%d\n",&N);
	scanf("%s",s);for(i=0;i<N;i++) A[i+1]=s[i];
	scanf("%s",s);for(i=0;i<N;i++) B[i+1]=s[i];
	scanf("%s",s);for(i=0;i<N;i++) C[i+1]=s[i];
	dfs(N,0);
	return 0;
}

你可能感兴趣的:(NOIP2004虫食算)