NOIp2004 虫食算(搜索剪枝)

洛谷P1092虫食算

题意:

N进制加法,三个数都是N位,计算出使式子成立的每个字母分别所代表的值.((N<=26))

如图是N=4的一个例子,当ABCD分别代表0123时,式子成立.

本题的正解应该是高斯消元,但搜索+剪枝足够应付过去.

直接搜索,枚举每一个字母分别代表的值,这样显然会超时,考虑如何剪枝.

剪枝1:题目中说了三个数都是N位,说明最高位没有进位.

(假设A,B分别是两个加数的最高位)
     则当A+B>=N时,不成立;

剪枝2:因为是加法运算,所以最多进1.

(假设A,B,C是一一对应的任一位上)
     则当(A+B)%N!=C&&(A+B+1)%N!=C时,不成立;
     
int n,order;
int used[30],num[30],nxt[30];
int a[30],b[30],c[30];
char s1[30],s2[30],s3[30];
void NEXT(int x){
    if(used[x])return;
    used[x]=1;
    nxt[order++]=x;
    return;
}
inline bool jz(){//剪枝
    if(num[a[0]]+num[b[0]]>=n)return true;
//对应剪枝二
    for(int i=n-1;i>=0;i--){
		int A=num[a[i]];
		int B=num[b[i]];
        int C=num[c[i]];
		if(A==-1||B==-1||C==-1)continue;
		if((A+B)%n!=C&&(A+B+1)%n!=C)
        	return true;
    }
//对应剪枝一
    return false;
}
inline bool cheek(){//检验式子是否成立
    for(int i=n-1,x=0;i>=0;i--){
		int A=num[a[i]];
        int B=num[b[i]];
        int C=num[c[i]];
		if((A+B+x)%n!=C)return false;
		x=(A+B+x)/n;
    }
//x表示进位
    return true;
}
void print(){//输出
    for(int i=0;i<n;i++)
		printf("%d ",num[i]);
    exit(0);
}
inline void dfs(int x){
    if(jz()==true)return;
    if(x==n){
		if(cheek()==true)print();
		return;
    }
    for(int i=n-1;i>=0;i--){
		if(used[i]==1)continue;
		used[i]=1;
		num[nxt[x]]=i;
		dfs(x+1);
		used[i]=0;
		num[nxt[x]]=-1;
    }
}
int main(){
    n=read();
    scanf("%s%s%s",s1,s2,s3);
    for(int i=0;i<n;i++){
    	a[i]=s1[i]-'A';
        b[i]=s2[i]-'A';
        c[i]=s3[i]-'A';
    }
//将三个字符串转为整型数字分别存入三个数组中
    for(int i=n-1;i>=0;i--){
    	NEXT(a[i]);
        NEXT(b[i]);
        NEXT(c[i]);
    }
//next[x]表示整个式子从上到下,从右到左(从低位到高位)
//每个字母(已经转换为了数字)出现的顺序
//这也是我们搜索时赋值的顺序,即从低位到高位,便于处理
    memset(num,-1,sizeof(num));
    memset(used,0,sizeof(used));
    dfs(0);
    return 0;
}

原文地址:https://www.cnblogs.com/PPXppx/p/10324283.html