[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. 其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同 ...
随机推荐
- 开启linux服务器防火墙
启用ufwsudo ufw enablesudo ufw default deny运行以上两条命令后,开启了防火墙,并在系统启动时自动开启.关闭所有外部对本机的访问,但本机访问外部正常. 开启和禁用s ...
- C语言 --- 高级指针
1. 指针赋值: C语言允许使用赋值运算进行指针的赋值,前提是两个指针具有相同的类型. int i,*p,*q; p = &i; ...
- C++中函数异常规格的说明
1,本文介绍一个新的概念,它是一个重要的概念,并且是 C++ 中的一个高级主题: 2,问题: 1,如何判断一个函数(不是自己写的,有可能是第三方库中的函数)是否会抛出异常,以及抛出那些异常? 1,学习 ...
- mysql自增主键字段重排
不带外键模式的 mysql 自增主键字段重排 1.备份表结构 create table table_bak like table_name; 2.备份表数据 insert into table_bak ...
- 软考题型—PERT图(项目计划评审技术)
经历过软件危机和大量软件项目的失败,人们对软件工程产业现状进行分析后,得出普遍性结论便是:软件项目成功率低的原因很可能就是项目管理能力太弱.由于软件本身的特殊性和复杂性,将项目管理思想引入软件工程领域 ...
- Comet OJ - Contest #5 E 迫真大游戏
怎么说,看了推到之后真的不难,事实上确实也蛮友好(可能咱就是想不出多项式题目的做法???),除了用到了分治法法塔就比较毒瘤 花了一个晚上以及一个上午做这么一道题...(还是太菜了) Result1 分 ...
- Jquery复习(二)之stop()易忘点
jQuery stop() 方法 jQuery stop() 方法用于停止动画或效果,在它们完成之前. stop() 方法适用于所有 jQuery 效果函数,包括滑动.淡入淡出和自定义动画. 语法:$ ...
- wex5 如何使用蓝牙 ble
使用蓝牙插件 需要在js中添加 require("cordova!cordova-plugin-ble-central"); ble插件具体文档: http://docs.we ...
- PHP foreach 引用 &
以前用foreach,总喜欢在第二次遍历时改变value的拼写,比如 $x = array("a", "b", "c"); foreach ...
- java学习笔记(5)多线程
一.简介(过段时间再写,多线程难度有点大) --------------------------------------- 1.进程:运行时的概念,运行的应用程序 2.线程:应用程序内部并发执行的代码 ...