[BZOJ1902]:[NOIP2004]虫食算(搜索)
题目传送门
题目描述
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。
来看一个简单的例子:
43#98650#45+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个不同的字母分别代表的数字,使得该加法算式成立。输入数据保证有且仅有一组解。
输入格式
包含4行。第一行有一个正整数N,后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
输出格式
包含一行。在这一行中,应当包含唯一的那组解。解是这样表示的:输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
样例
样例输入:
5
ABCED
BDACE
EBBAA
样例输出:
1 0 3 4 2
数据范围与提示
对于全部的数据,保证有N≤26。
题解
官方正解是高斯消元,不得不承认高斯消元用来做这道题的确要比搜索简单。
先来算一下时间复杂度,高斯消元:O(${n}^{3}$),搜索:O(${n}^{n}$)。
不管你怎么说,我感觉${n}^{n}$挺帅的。
所以我就去搜索了哇~
显然不能直接爆搜,卡长卡的好没准能跑过n=9,n≥10想都别想了。
那么考虑剪枝:
为简便,地一个字符串称为A,第二个字符串称为B,第三个字符串称为C。
首先你肯定会想到,如果A+B肯定不能得到C,那么就不要往下搜了。
这里需要注意的是,有可能会存在进位。
然后接着考虑,小学的时候数学老师教给你竖式加法的时候,我们都是从最右边网左算。
那么转换到这道题,我们也可以从右往左搜,这样就能更快的把枝剪掉了(注意你的judge函数要优秀)。
还有一种情况,就是如果最前面出现了进位,那么是要剪掉的,所以我们先搜大的数可以更快的出现进位,进而更快的剪掉这个枝。
所以从大往小搜必不可少。
有了这些我就可以轻松A掉这道题了,但是作为一名卓越的OIER,我们还要考虑怎么让代码跑的更快。
所以我就讲一下所有的剪枝:
1.最前面出现进位直接return false,或者A和B串开头位相加比C串开头位大,也要return false。
2.从左往右开始judge,首先,在A,B,C串都没有出现有字母没有赋值的情况的时候,可以按照严格的带进位的加法进行判断。
3.在出现了一个数没有赋值之后,我们就要按照考虑进位与不进位开始考虑了。
$\alpha$.最好想到的就是无论进不进位都无法满足的话,就return false。
$\beta$.如果当前位有两个数已经赋值,一个数还没有赋值,那么如果根据这两个数所算出来的第三个数已经被使用(赋值不能重复),那么可以提前剪掉。
如果这些都能满足的话,则继续往前搜。
代码时刻
#include<bits/stdc++.h>
using namespace std;
bool vis[27];
int wzc[27];
int ans[27];
int n;
int a[27],b[27],c[27];
int sor[27],cnt;
char ch1[27],ch2[27],ch3[27];
inline bool _wzc()//judge
{
int wzc=0,_=0;
if(ans[a[0]]+ans[b[0]]>=n)return 0;//判断开头进不进位
if(ans[c[0]]!=-1)//还是在开头剪枝
{
if(ans[a[0]]>ans[c[0]]||ans[b[0]]>ans[c[0]])return 0;
if(ans[a[0]]!=-1&&ans[b[0]]!=-1&&ans[a[0]]+ans[b[0]]!=ans[c[0]]&&ans[a[0]]+ans[b[0]]+1!=ans[c[0]])return 0;
}
for(int i=n-1;i>=0;i--)//全都被赋过值
{
if(ans[a[i]]==-1||ans[b[i]]==-1||ans[c[i]]==-1)//出现有没有被赋过值的
{
_=i;
break;
}
if((ans[a[i]]+ans[b[i]]+wzc)%n!=ans[c[i]])return 0;//严格带进未搜索
wzc=(ans[a[i]]+ans[b[i]]+wzc)/n;
}
for(int i=_;i>=0;i--)//开始考虑进位与不进位,以下分情况,注意代码中“!=”和“==”。
{
if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
if((ans[a[i]]+ans[b[i]])%n!=ans[c[i]]&&(ans[a[i]]+ans[b[i]]+1)%n!=ans[c[i]])return 0;
if(ans[a[i]]!=-1&&ans[b[i]]!=-1&&ans[c[i]]==-1)
if((vis[(ans[a[i]]+ans[b[i]])%n])&&vis[(ans[a[i]]+ans[b[i]]+1)%n])return 0;
if(ans[a[i]]!=-1&&ans[b[i]]==-1&&ans[c[i]]!=-1)
if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
if(ans[a[i]]==-1&&ans[b[i]]!=-1&&ans[c[i]]!=-1)
if((vis[ans[c[i]]-ans[a[i]]])&&vis[ans[c[i]]+ans[a[i]]-1]&&vis[c[i]+n-ans[a[i]]]&&vis[c[i]+n-ans[a[i]]-1])return 0;
}
return 1;
}
void dfs(int x)
{
if(!_wzc())return;
if(x==n)
{
if(_wzc())
{
for(int i=0;i<n;i++)
printf("%d ",ans[i]);
exit(0);//搜到结果直接结束
}
}
for(int i=n-1;i>=0;i--)//反向搜
{
if(!vis[i])
{
vis[i]=1;
ans[sor[x]]=i;
dfs(x+1);
vis[i]=0;
ans[sor[x]]=-1;
}
}
return;
}
int main()
{
scanf("%d",&n);
scanf("%s%s%s",ch1,ch2,ch3);
for(int i=0;i<n;i++)
{
a[i]=ch1[i]-'A';
b[i]=ch2[i]-'A';
c[i]=ch3[i]-'A';
ans[i]=-1;
}
for(int i=n-1;i>=0;i--)//统计搜索顺序
{
if(!vis[a[i]])
{
vis[a[i]]=1;
sor[cnt++]=a[i];
}
if(!vis[b[i]])
{
vis[b[i]]=1;
sor[cnt++]=b[i];
}
if(!vis[c[i]])
{
vis[c[i]]=1;
sor[cnt++]=c[i];
}
}
memset(vis,0,sizeof(vis));
dfs(0);
return 0;
}
rp++
[BZOJ1902]:[NOIP2004]虫食算(搜索)的更多相关文章
- NOIP2004 虫食算
描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子:43#9865#045+ 8468#6633= 44445506678其中#号代表 ...
- Luogu1092 NOIP2004虫食算(搜索+高斯消元)
暴力枚举每一位是否进位,然后就可以高斯消元解出方程了.然而复杂度是O(2nn3),相当不靠谱. 考虑优化.注意到某一位进位情况的变化只会影响到方程的常数项,于是可以在最开始做一次高斯消元算出每个未知数 ...
- [Noip2004]虫食算 dfs
搜索问题的关键:优秀的搜索策略以及行之有效的减枝 对于这道题我们阶乘搜肯定不行所以我们按位搜,我们对每一位的三个数进行赋值,然后判解. 对于此一类的搜索乘上一个几十的常数来减枝往往要比直接搜要快得多, ...
- P1092 虫食算[搜索]
这个式子是是由\(A\sim A+N\)组成的,那么\(A\sim A+N\)就只能等于\(0\sim N-1\),因此我们每次对\(A\sim A+N\)的取值做一个新的排列,然后judge一下当前 ...
- NOIP 2004 虫食算题解
问题 E: [Noip2004]虫食算 时间限制: 1 Sec 内存限制: 128 MB 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一 ...
- [NOIP2004] 提高组 洛谷P1092 虫食算
题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 其中# ...
- 【NOIP2004】【CJOJ1703】【洛谷1092】虫食算
题面 题目描述 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +8468#6633 44445509678 ...
- 【NOIP2004】虫食算
Description 所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母.来看一个简单的例子: 43#9865#045 +. 8468#6633 444455 ...
- 【搜索】$P1092$虫食算
题目链接 首先,我们只考虑加法的虫食算.这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同 ...
随机推荐
- Head First PHP&MySQl第二章代码
PHP: <html> <head> <title>外星人绑架了我--报道一起绑架</title> </head> <body> ...
- 02:Redis常见面试题
1.1 redis基础面试题 1.什么是Redis?简述它的优缺点? 1. Redis本质上是一个Key-Value类型的内存数据库,很像memcached. 2. 整个数据库统统加载在内存当中进行操 ...
- HNUST-1047 二叉树的表示
1047: 二叉树的表示 时间限制: 1 Sec 内存限制: 128 MB提交: 4 解决: 4[提交][状态][讨论版] 题目描述 DJ非常痴迷于数据结构,二叉树是他最喜欢的结构模型.这种每个 ...
- 一个php文件就可以把数据库的数据导出Excel表格
数据库内容太多,复制粘贴太麻烦?那就用代码实现把,把代码写好了,导出还不容易吗,访问即可导出. excel.php <?php error_reporting(E_ALL ^ E_DEPRECA ...
- linux:用户和组文件解释(/etc/passwd、/etc/shadow、/etc/group、/etc/gshadow)
一.用户文件:/etc/passwd [root@pinfun6 ~]# cat /etc/passwd root:x:0:0:root:/root:/bin/bash 1 2 3 4 5 6 7 | ...
- 使用procedump捕获未处理异常的dump
-ma full memory dump, always do this on 2003 as 4gb is not much and it is good to have the heap -mp ...
- css阴影——box-shadow
1.语法 box-shadow: h-shadow v-shadow blur spread color inset; box-shadow: 水平阴影 垂直阴影 模糊距离 阴影大小 阴影 ...
- MEWKit:Cryptotheft 的最新武器
By:Yonathan Klijinsma 译者:知道创宇安全服务团队.404区块链安全团队 介绍 当谈到加密货币时,会联想到加密货币巨大的价格波动,交易违约.赎金勒索的情况以及许多不同种类的货币.虚 ...
- [工具] BurpSuite--Intruder功能
BurpSuite--Intruder功能 0x00 配置说明 intruder是进行爆破的,基本流程是标注请求的爆破参数,然后配置字段,选择爆破方式进行爆破,下面来记录下工具的使用 选中intrud ...
- nslookup 工具的使用方法记录
查询IP地址 nslookup最简单的用法就是查询域名对应的IP地址,包括A记录和CNAME记录,如果查到的是CNAME记录还会返回别名记录的设置情况.其用法是: nslookup 域名 定查询记录类 ...