虫食算

题目描述

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

 43#9865#045
+  8468#6633
 44445509678

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

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

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

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

 BADC
+CBDA
 DCCC

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

输入输出格式

输入格式:

包含四行。
第一行有一个正整数N((N≤26)。

后面的三行,每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。

输出格式:

一行,即唯一的那组解。

解是这样表示的:输出N个数字,分别表示A,B,C,…所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。

输入输出样例

输入样例#1

5
ABCED
BDACE
EBBAA

输出样例#1:

1 0 3 4 2

很容易想到dfs

一开始的想法:将ABCD…分别全部赋值 再进行计算

#include<stdio.h>
#include<string.h>
int N;
int used[28];
char left[28];
char right[28];
char sum[28];
int cast[30];
int ans[30];
int leap;
void search(int t)
{
    
    int i,j;
    if(leap==1)
    {
        return ;
    }
    else if(t==N)
    {
        if(cast[left[0]-'A']+cast[right[0]-'A']>=N)          最高位不能进位
            return ;
        memset(ans,0,sizeof(int)*N);
        leap=1;
        for(i=N-1,j=0;i>=0;i--,j++)
        {
            ans[j]+=cast[left[i]-'A']+cast[right[i]-'A'];
            if(ans[j]>=N)
            {
                ans[j+1]=ans[j]/N;
                ans[j]%=N;
            }
            if(ans[j]!=cast[sum[i]-'A'])
            {
                leap=0;
                return ;
            }
        }
        if(leap==1)
        {
            for(i=0;i<N;i++)
                printf("%d ",cast[i]);
        }
    }
    else
    {
        for(i=0;i<N;i++)
        {
            if(!used[i])
            {
                used[i]=1;
                cast[t]=i;
                search(t+1);
                used[i]=0;
            }
        }
    }
}
int main(void)
{
    scanf("%d",&N);
    scanf("%s%s%s",left,right,sum);
    search(0);
}

发现时间复杂度太高并不能AC

后来琢磨了洛谷题解中大佬的代码和思路:dfs+剪枝

从右往左从上到下搜索 一个个位置进行赋值(即碰到E若E未赋值则搜索进行赋值 若E已赋值则继续搜索)

搜索的过程中进行剪枝提前去掉不成立的式子

#include<stdio.h>
#include<string.h>
int n;
char s[4][30];                          记录三个字符串
int used[30];                           记录数字是否使用  
int cast[30];                           记录字母是否使用
int leap;								是否找到正确答案
int change(char c)
{
	return c-'A';                       将字母转换为数字
}
void search(int x,int y,int t)          x表示行,y表示列,t表示进位
{
	int i;
	if(leap==1)                         如果已经有正确值则结束
		return ;
	if(y==-1)                           如果能运行到-1列则说明式子正确
	{
		if(t==0)                        最高位不能有进位 
		{
			for(i=0;i<n;i++)
				printf("%d ",cast[i]);
			leap=1;
			return ;
		}
		return ;
	}
	for(i=y;i>=0;i--)					 减小时间复杂度重点:剪枝
	{
		int a1=cast[change(s[1][i])];
		int a2=cast[change(s[2][i])];
		int a3=cast[change(s[3][i])];
		if(a1==-1||a2==-1||a3==-1)
			continue;
		if((a1+a2)%n!=a3&&(a1+a2+1)%n!=a3)
			return ;
	}
	if(cast[change(s[x][y])]==-1)         如果该位置代表的字母未赋值
	{
		for(i=n-1;i>=0;i--)               PS:从n-10会快一些
		{
			if(!used[i])
			{
				if(x!=3)                  如果不是第三行 搜索+回溯
				{
					cast[change(s[x][y])]=i;
					used[i]=1;
					search(x+1,y,t);      搜索该列的下一行
					cast[change(s[x][y])]=-1;
					used[i]=0;
				}
				else
				{
					int sum=cast[change(s[1][y])]+cast[change(s[2][y])]+t;
					if(sum%n!=i)           说明i不能满足上两个数相加
						continue;
					used[i]=1;
					cast[change(s[x][y])]=i;
					search(1,y-1,sum/n);   搜索前一列的第一行,同时修改进位
					used[i]=0;
					cast[change(s[x][y])]=-1;
				}
			}
		}
	}
	else
	{
		if(x!=3)
		{
			search(x+1,y,t);
		}
		else
		{
			int sum=cast[change(s[1][y])]+cast[change(s[2][y])]+t;
			if(sum%n!=cast[change(s[3][y])])   判断这一列的式子是否正确 第二个剪枝
				return ;
			search(1,y-1,sum/n);
		}
	}
}
int main(void)
{
	scanf("%d",&n);
	scanf("%s%s%s",s[1],s[2],s[3]);
	memset(cast,-1,sizeof(int)*n);
	search(1,n-1,0);	                       从右往左从上到下 即从第一行的第n-1列开始搜索(即最后一列)
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值