[NOIP2004提高组]虫食算

题目:洛谷P1092、codevs1064、Vijos P1099。

题目大意:
给你一个$n$进制、每个数都是$n$位的三个数a,b,c,这些数的数位由字母表示(共$n$个字母,从‘A’开始),所有数字都只对应一个字母,每个字母对应一个数字。
现在知道a+b=c,求每个字母代表的数字,保证有且仅有一组解。
解题思路:
暴力,从后往前搜索每一列即可。注意传递进位。
若一列知道了三个数,则判断是否合法,合法则继续搜索下一列。
若一列知道了两个数,则可以推算出第三个数,若没被使用,则继续搜索下一列。
若一列知道的数少于一个,则a或b中,一定有一个数字是不知道的。我们从大到小枚举那个数的值,然后搜**当前列**(递归)。
如果发现答案可行,则直接输出答案,退出即可。
这样搜,不用加其他剪枝,最大一个点最多800ms(其实上述分类讨论已经很好地进行了剪枝)。

C++ Code:

#include<bits/stdc++.h>
int n,num[123],ur[123];
char a[123],b[132],c[213];
void dfs(int now,int jw){
	if(now==-1){
		for(int i=1;i<n;++i)printf("%d ",num[i+'A'-1]);
		printf("%d\n",num[n+'A'-1]);
		exit(EXIT_SUCCESS);
	}
	int i=now;
	//if(now!=n-1&&!check(now+1))return;
	if(num[a[i]]!=-1&&num[b[i]]!=-1&&num[c[i]]!=-1){
		int p=num[a[i]]+num[b[i]]+jw;
		if(p%n==num[c[i]]){
			dfs(now-1,p/n);
		}
		return;
	}
	if(num[a[i]]!=-1&&num[b[i]]!=-1){
		int p=num[a[i]]+num[b[i]]+jw;
		if(!ur[p%n]){
			ur[p%n]=1;
			num[c[i]]=p%n;
			dfs(now-1,p/n);
			num[c[i]]=-1;
			ur[p%n]=0;
		}
		return;
	}
	if(num[a[i]]!=-1&&num[c[i]]!=-1){
		int p=num[c[i]]-jw-num[a[i]]+n;
		if(!ur[p%n]){
			ur[p%n]=1;
			num[b[i]]=p%n;
			dfs(now-1,!(p/n));
			num[b[i]]=-1;
			ur[p%n]=0;
		}
		return;
	}
	if(num[b[i]]!=-1&&num[c[i]]!=-1){
		int p=num[c[i]]-jw-num[b[i]]+n;
		if(!ur[p%n]){
			ur[p%n]=1;
			num[a[i]]=p%n;
			dfs(now-1,!(p/n));
			num[a[i]]=-1;
			ur[p%n]=0;
		}
		return;
	}
	if(num[a[i]]!=-1){
		for(int j=n-1;j>-1;--j)
		if(!ur[j]){
			ur[j]=1;
			num[b[i]]=j;
			dfs(now,jw);
			num[b[i]]=-1;
			ur[j]=0;
		}
	}else{
		for(int j=n-1;j>-1;--j)
		if(!ur[j]){
			ur[j]=1;
			num[a[i]]=j;
			dfs(now,jw);
			num[a[i]]=-1;
			ur[j]=0;
		}
	}
}
int main(){
	scanf("%d%s%s%s",&n,a,b,c);
	memset(ur,0,sizeof ur);
	memset(num,-1,sizeof num);
	dfs(n-1,0);
	return EXIT_FAILURE;
}

猜你喜欢

转载自www.cnblogs.com/Mrsrz/p/9026478.html