P1092 虫食算——题解

题目传送

(据说官方正解为高斯消元,但用搜索也能过,这里就讲讲搜索算法吧。)

  对于一道搜索题,首先考虑一下大体怎样搜索。因为要考虑加法的进位,所以从左往右搜索对于考虑进位来说十分麻烦,而从右往左搜索就没有这种麻烦,故搜索顺序从右往左。但是发现整个式子的一位上由三个字符串的一位组成,且这三个分别担当加数、结果中的一部分,逐个搜索的话还要麻烦地分类讨论,考虑再优化一下搜索顺序。发现一共只有n个不同的数和字母,并且每个数和字母都至少出现一次,那么可以从右往左找出字母第一次出现的位置并存到next数组里,只要按照next数组搜一遍就完事了。

  显然一个裸搜在此题肯定会TLE了,考虑一下剪枝。由于每个字符串都有n位,那么作为加数的两个字符串的最高位加进位加起来一定不能大于等于n导致进位。同时如果发现在同一位的数若都已经确定,发现两加数那一位上的数的和mod n(不进位的情况)不等于结果那一位上的数,并且两加数那一位上的数的和+1再mod n(进位的情况)仍然不等于结果那一位上的数,显然是不合题意的,故要剪掉。

  一个小技巧:发现相同的字母代表相同的数。我们可以把字母数字化,即每个字母都减‘A‘加1,这样若原字母为‘A‘,处理后就为1,若为‘B‘,就为2,...以此类推。这样可以让处理后的值作为最后记录答案的数组num的下标,同一个字母处理后的变为的下标也一样,故可以统一处理。

见AC代码:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5
  6 using namespace std;
  7
  8 int n;
  9
 10 char s1[28],s2[28],s3[28];
 11
 12 int a[28],b[28],c[28],next[28],cnt,num[28];
 13
 14 bool used[28];
 15
 16 inline void getused(int x)//处理出next数组
 17 {
 18     if(!used[x])
 19     {
 20         used[x]=1;
 21         next[++cnt]=x;
 22     }
 23 }
 24
 25 inline int canprune()//剪枝判断  prune:剪枝(来自翻译)
 26 {
 27     if(num[a[1]]+num[b[1]]>=n)
 28         return 1;
 29     int A,B,C;
 30     for(int i=n;i>=1;i--)
 31     {
 32         A=num[a[i]],B=num[b[i]],C=num[c[i]];
 33         if(A==-1||B==-1||C==-1) continue;
 34         if((A+B)%n!=C&&(A+B+1)%n!=C) return 1;
 35     }
 36     return 0;
 37 }
 38
 39 inline int judge()//结果判断
 40 {
 41     if(num[a[1]]+num[b[1]]>=n)
 42         return 0;
 43     int A,B,C,x=0;
 44     for(int i=n;i>=1;i--)
 45     {
 46         A=num[a[i]],B=num[b[i]],C=num[c[i]];
 47         if((A+B+x)%n!=C) return 0;
 48         x=(A+B+x)/n;
 49     }
 50     return 1;
 51 }
 52
 53 void print()
 54 {
 55     for(int i=1;i<n;i++)
 56         printf("%d ",num[i]);
 57     cout<<num[n];
 58     exit(0);//要用 stdlib.h 或 cstdlib库(少了头文件的话本地虽然能过,但交上去就回CE),作用是直接退出程序。
 59 }
 60
 61 void dfs(int k)
 62 {
 63     for(int i=n-1;i>=0;--i)
 64     if(!used[i])
 65     {
 66         num[next[k]]=i;
 67         used[i]=1;
 68         if(k==n)
 69         {
 70             if(judge())
 71                 print();
 72         }
 73         else
 74         {
 75             if(!canprune())
 76                 dfs(k+1);
 77         }
 78         num[next[k]]=-1;
 79         used[i]=0;
 80     }
 81 }
 82
 83 int main()
 84 {
 85     scanf("%d",&n);
 86     scanf("%s%s%s",s1,s2,s3);
 87     for(int i=n;i>=1;--i)
 88     {
 89         a[i]=s1[i-1]-‘A‘+1;
 90         getused(a[i]);
 91         b[i]=s2[i-1]-‘A‘+1;
 92         getused(b[i]);
 93         c[i]=s3[i-1]-‘A‘+1;
 94         getused(c[i]);
 95     }
 96     memset(num,-1,sizeof num);
 97     memset(used,0,sizeof used);
 98     dfs(1);
 99     return 0;
100 }

原文地址:https://www.cnblogs.com/InductiveSorting-QYF/p/11181367.html

时间: 2024-10-14 12:02:07

P1092 虫食算——题解的相关文章

P1092虫食算题解

2018-10-24 题目链接 题目思路: 我就讲下剪枝操作吧. 三个式子从上到下为A,B,C 剪枝操作 1.从3个式子右边开始从上到下枚举字母对应数字.(搜索顺序关键) 2.末尾(A+B)%n!=C. 3.已知A,B,不知C,且C的可能数字已经使用.(这三个点从2000ms) 4.已知A.C,不知B,且B的可能数字已经使用.(一下剪枝成) 5.已知B,C,不知A,且A的可能数字已经使用.(100ms) 6.已知A,B,C,且A+B不等于C,且A+B+1不等于C. 7.已经A,B,C的最高位,且

洛谷 P1092 虫食算

P1092 虫食算 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: http://paste.ubuntu.com/25448822/ 其中#号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5. 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,

Luogu P1092 虫食算(枚举+剪枝)

P1092 虫食算 题面 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 + 8468#6633 44445509678 其中 \(\#\) 号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是 \(5\) 和 \(3\) ,第二行的数字是 \(5\) . 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是 \(N\) 进制加法,算式中三个数都有 \(N\

[NOIP2004] 提高组 洛谷P1092 虫食算

题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 其中#号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5. 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用

洛谷—— P1092 虫食算

https://www.luogu.org/problem/show?pid=1092 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: http://paste.ubuntu.com/25448822/ 其中#号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5. 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前

洛谷 P1092 虫食算 Label:dfs

题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 其中#号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5. 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用

Luogu P1092 虫食算

题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 其中#号代表被虫子啃掉的数字.根据算式,我们很容易判断:第一行的两个数字分别是5和3,第二行的数字是5. 现在,我们对问题做两个限制: 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用

洛谷P1092虫食算——深搜

题目:https://www.luogu.org/problemnew/show/P1092 剪枝1:从右往左.从上往下按字母出现顺序搜索: 剪枝2:同一列前两个数字确定,可直接算出第三个数字并判断: 剪枝3:每次搜索前看看前面的列上有没有已经不符合的情况(进位最多为1): 代码如下: #include<iostream> #include<cstdio> using namespace std; int n,c[300],jin[30]; char a[5][30]; bool

LUGOU P1092 虫食算

传送门 解题思路 刚开始按yzy神犇给的方法写,就是每次要把能算出来的都算出来,结果因为太菜写挂了..后来直接爆搜水过.. #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> using namespace std; const int MAXN=40; int n,a[MAXN],cnt; char s[4][MAXN];