alpha.docx
- 文档编号:25088496
- 上传时间:2023-06-05
- 格式:DOCX
- 页数:30
- 大小:28.88KB
alpha.docx
《alpha.docx》由会员分享,可在线阅读,更多相关《alpha.docx(30页珍藏版)》请在冰豆网上搜索。
alpha
虫食算解题报告
<问题描述>
所谓虫食算,就是原先的算式中有一部分被虫子啃掉了,需要我们根据剩下的数字来判定被啃掉的字母。
来看一个简单的例子:
43#9865#045
+ 8468#6633
其中#号代表被虫子啃掉的数字。
根据算式,我们很容易判断:
第一行的两个数字分别是5和3,第二行的数字是5。
现在,我们对问题做两个限制:
首先,我们只考虑加法的虫食算。
这里的加法是N进制加法,算式中三个数都有N位,允许有前导的0。
其次,虫子把所有的数都啃光了,我们只知道哪些数字是相同的,我们将相同的数字用相同的字母表示,不同的数字用不同的字母表示。
如果这个算式是N进制的,我们就取英文字母表午的前N个大写字母来表示这个算式中的0到N-1这N个不同的数字:
但是这N个字母并不一定顺序地代表0到N-1)。
输入数据保证N个字母分别至少出现一次。
BADC
+ CRDA
DCCC
上面的算式是一个4进制的算式。
很显然,我们只要让ABCD分别代表0123,便可以让这个式子成立了。
你的任务是,对于给定的N进制加法算式,求出N个不同的字母分别代表的数字,使得该加法算式成立。
输入数据保证有且仅有一组解,
-输入文件
输入文件alpha.in包含4行。
第一行有一个正整数N(N<=26),后面的3行每行有一个由大写字母组成的字符串,分别代表两个加数以及和。
这3个字符串左右两端都没有空格,从高位到低位,并且恰好有N位。
-输出文件
输出文件alpha.out包含一行。
在这一行中,应当包含唯一的那组解。
解是这样表示的:
输出N个数字,分别表示A,B,C……所代表的数字,相邻的两个数字用一个空格隔开,不能有多余的空格。
-样例输入
5
ABCED
BDACE
EBBAA
-样例输出
10342
-数据规模
对于30%的数据,保证有N<=10;
对于50%的数据,保证有N<=15;
对于全部的数据,保证有N<=26。
<算法分析>
经典的搜索题。
最单纯的搜索的时间复杂度为O(n!
),是会非常严重的超时的。
计算机是很“笨”的,它不会思考,在盲目搜索的过程中,很容易出现这种情况:
计算机在某一位搜索出了一个算式1+1=3,并且继续搜索。
明显,人眼很容易就看出这是不合法的,但计算机不会。
于是,我们想到了第一个剪枝:
每次搜索的时候,从最后向前判断是否有不合法的式子。
这一个剪枝非常简单,但是效果却非常的好。
因为它剪去了很多不必要的搜索。
为了配合这一种剪枝更好的实行,搜索顺序的改变也成为大大提高程序效率的关键:
从右往左,按照字母出现顺序搜索,有很大程度上提高了先剪掉废枝的情况,使程序的效率得到大大的提高。
有了以上两个剪枝,程序就已经可以通过大部分测试点了。
但是有没有更多的剪枝呢?
答案是肯定的。
根据前面的剪枝,我们可以找到类似的几个剪枝:
对于a+b=c的形式,假如:
A***?
***
+B*?
**?
**
C***?
?
?
*
其中*代表已知,?
代表未知。
那么,A+B与C的情况并不能直接确定。
但是,假如(A+B)%N与(A+B+1)%N都不等于C的话,那么这个等式一定是不合法的。
因为它只有进位和不进位的两种情况。
同样,我们在一个数组里记录了Used[i]表示一个数字有没有用过,那么,对于某一位A+B=C的等式,如果已经得到了两个数,另一个数还待搜索的时候,我们还可以根据这个加入一个剪枝:
例如A+?
=C的形式,
考虑不进位的情况,则?
处为P1=(C-A+N)%N
假如考虑进位的情况,则?
处为P2=(C-A-1+N)%N
假如P1、P2均被使用过,那么这个搜索一定是无效的,可以剪去。
有了以上的剪枝,就可以很轻松地通过所有的测试数据了。
当然,还有很多值得思考的剪枝以及其他的思路,例如枚举进位、解方程(但是可能需要枚举)等,在这里就不详细讨论了。
<代码清单>
{此题比较暴力==
暴力都会写吧==
但如果纯暴力会TLE
于是要加两个优化
一个是如果一个数的局部已经造成不符题意(见110行),就直接退出
另一个是按照字母出现的顺序来搜索(orzpz和s做的这个工作)
可以提高效率
}
vars1,s2,s3,s:
string;
n:
integer;
has:
array[0..27]ofinteger;
ans:
array['a'..'z']ofinteger;
orzpz:
array[0..256]ofboolean;
//orzpz,s是用来算字母出现先后顺序的
//按照顺序搜效率可能会稍微高一些
//(似乎不写会TLE几个点--)
used:
array[0..27]ofboolean;
a:
array[1..27]ofinteger;
functionmadd:
boolean;//n进制“低精度”加法的整体验证(跟高精度写法差不多==)
//exittrue为不符合题意
varp,q,i,b1,b2,b3:
integer;
begin
p:
=0;q:
=0;//q:
上一次的进位
fori:
=ndownto1do
begin
b1:
=ord(s1[i])-48;
b2:
=ord(s2[i])-48;
b3:
=ord(s3[i])-48;
if(b1>=n)or(b2>=n)or(b3>=n)thenexit(false);
p:
=b1+b2+q;
if(pmodn)<>b3thenexit(true);
q:
=pdivn;
end;
exit(false);
end;
functioncheck:
boolean;//作用与原理见110行==
//exittrue代表不符题意
varp,q,i,b1,b2,b3:
integer;a,b,c:
array[1..27]ofinteger;
begin
check:
=false;
fori:
=1tondo
begin
a[i]:
=ord(s1[i])-48;
b[i]:
=ord(s2[i])-48;
c[i]:
=ord(s3[i])-48;
end;
fori:
=ndownto1do
begin
if(a[i]>=n)or(b[i]>=n)or(c[i]>=n)thencontinue;
p:
=(a[i]+b[i])modn;
if(p<>c[i])and((p+1)modn<>c[i])then//由于不能判定是否有进位(其实可以判定)所以把p与p+1都检测一次--
exit(true);
end;
//以下三组fori=ndonwto1
//为单独检测,见dfs中引用check的部分(110行)
fori:
=ndownto1do
begin
ifnot((a[i]
p:
=(c[i]-a[i]+n)modn;
q:
=(p-1+n)modn;
if(used[p]andused[q])then
exit(true);
end;
fori:
=ndownto1do
begin
ifnot((b[i]
p:
=(c[i]-b[i]+n)modn;
q:
=(p-1+n)modn;
if(used[p]andused[q])then
exit(true);
end;
fori:
=ndownto1do
begin
ifnot((a[i]
p:
=(a[i]+b[i]+n)modn;
q:
=(p+1)modn;
if(used[p]andused[q])then
exit(true);
end;
end;
procedurechange(varw:
string;c:
char;d:
integer);//把W字符串中的所有c字符转换成d
varj:
integer;
begin
forj:
=1tondo
ifw[j]=cthen
w[j]:
=chr(d+48);
end;
procedurepans;//输出答案
vari:
integer;c:
char;
begin
fori:
=1tondo
ans[s[i]]:
=has[i];
forc:
='a'tochr(ord('a')-1+n)do
write(ans[c],'');
writeln;
close(input);
close(output);
halt;
end;
proceduredfs(e:
integer);//暴搜第N个字母
vari,j:
integer;
t1,t2,t3:
string;
begin
ifmaddthenexit;//整体验证
ifcheckthenexit;//验证局部:
check函数
//如果在a+b=c中
//a,c的同一位a[i],c[i]已知
//则可以计算b[i](要考虑上一次进位或者不进位的情况)
//如果任何一种b[i]的那个数字已经被用过了就退掉
//a,b以及b,c已知的情况与此相同
ife=n+1thenpans;//如果已经搜到答案就输出
fori:
=n-1downto0do
ifnotused[i]then
begin
used[i]:
=true;//used:
某数是否已经用过
t1:
=s1;t2:
=s2;t3:
=s3;
has[e]:
=i;
change(s1,s[e],i);//搜索下一种情况字符
change(s2,s[e],i);
change(s3,s[e],i);
dfs(e+1);//搜索下一位
used[i]:
=false;
s1:
=t1;s2:
=t2;s3:
=t3;
end;
end;
procedureinit;
vari:
integer;
proceduregetit(c:
char);//获取字母出现顺序(或者叫搜索的优先级==)
vartmp:
integer;
begin
tmp:
=ord(c)-64;
ifnotorzpz[tmp]then
begin
orzpz[tmp]:
=true;
s:
=s+c;
end;
end;
begin
readln(n);
readln(s1);
readln(s2);
readln(s3);
fori:
=1tondo
begin
s1[i]:
=chr(ord(s1[i])+ord('a')-ord('A'));
s2[i]:
=chr(ord(s2[i])+ord('a')-ord('A'));
s3[i]:
=chr(ord(s3[i])+ord('a')-ord('A'));
end;
s:
='';
fillchar(orzpz,sizeof(orzpz),false);
fori:
=n-1downto0do
begin
getit(s1[i+1]);
getit(s2[i+1]);
getit(s3[i+1]);
end;
end;
begin
//assign(input,'alpha.in');
//assign(output,'alpha.out');
//reset(input);
//rewrite(output);
init;
dfs
(1);
end.
#include
#include
usingnamespacestd;
ifstreamfin("alpha.in");
ofstreamfout("alpha.out");
boolfinish,hash[256],used[27];
intn,stk[27];
stringa,b,c;
stringword;
voidinit(){
fin>>n>>a>>b>>c;
finish=false;
}
voidoutsol(){
inti,ans[27];
for(i=0;i ans[word[i]-65]=stk[i]; fout< for(i=1;i fout<<""< fout< finish=true; } voidaddup(charch){ if(! hash[ch]){ hash[ch]=true; word=word+ch; } } stringchange(stringstr,charx,chary){ for(inti=0;i if(str[i]==x) str[i]=y; returnstr; } voidpre_doing(){ word=""; memset(hash,0,sizeof(hash)); for(inti=n-1;i>=0;i--){ addup(a[i]);addup(b[i]);addup(c[i]); } memset(used,0,sizeof(used)); } boolbad(){ intp,g=0; for(inti=n-1;i>=0;i--){ if(a[i]>=n||b[i]>=n||c[i]>=n)returnfalse; p=a[i]+b[i]+g; if(p%n! =c[i])returntrue; g=p/n; p%=n; } returnfalse; } boolmodcheck(){ inti,p,p1,p2,g=0; //a+b=c,allknow for(i=n-1;i>=0;i--){ if(a[i]>=n||b[i]>=n||c[i]>=n)continue; p=(a[i]+b[i])%n; if(! (p==c[i]||(p+1)%n==c[i]))returntrue; } //a+? =c for(i=n-1;i>=0;i--){ if(! (a[i] p1=(c[i]-a[i]+n)%n; p2=(p1-1)%n; if(used[p1]&&used[p2])returntrue; } //? +b=c for(i=n-1;i>=0;i--){ if(! (a[i]>=n&&c[i] p1=(c[i]-b[i]+n)%n; p2=(p1-1)%n; if(used[p1]&&used[p2])returntrue; } //a+b=? for(i=n-1;i>=0;i--){ if(! (a[i] p1=(a[i]+b[i])%n; p2=(p1+1)%n; if(used[p1]&&used[p2])returntrue; } returnfalse; } voiddfs(intl){ inti; stringA,B,C; if(finish)return; if(bad())return; if(modcheck())return; if(l==n){ outsol(); return; } for(i=n-1;i>=0;i--) if(! used[i]){ used[i]=true;A=a;B=b;C=c; a=change(A,word[l],i); b=change(B,word[l],i); c=change(C,word[l],i); stk[l]=i; dfs(l+1); used[i]=false;a=A;b=B;c=C; } } intmain(){ init(); pre_doing(); dfs(0); return0; } <小结> 搜索题的框架往往不难找到,关键就是在搜索的优化上,本文的主要篇幅也就是讨论了几种有效的优化。 搜索问题的优化更多的需要选手的经验和思考、分析问题的能力,所以搜索剪枝也是竞赛中经久不衰的经典问题。 看看吧。 。 。 程序都是很快的 虫食算编程手记 By武钢三中王谟{WinterLegend} 虫食算这道题目,前前后后做了30个小时,这只是最保守的估计。 我的程序运行都很快,注释,分析很详细,值得一看。 {跳跃剪枝法(即,回溯到上一层之后继续回溯到上上一层,直到traceback层,然后将traceback赋为infinite。 故形象称之为跳跃剪枝法} programAlpha; varposi,ans: array['A'..'Z']oflongint;//用的位置(用于跳跃剪枝)、答案 {posi表示的是第一个位置,如果当前搜索到的字母有错,那么就跳跃回溯到这三个字母的第一次出现的列数的最大值 (这个地方,我认为可以优化,可以进一步确定到底是哪一个字母出了问题} ct: array[-30..30]ofboolean;//数字用了没有 v: array[-30..30]oflongint;{枚举进位,这个东西必须要用一个数组存起来,设dep(a)>dep(b),假如b到a两次,那么两次的进位会不一样。 v必须保存特定的一位的进位,如果只是表示当前位的前一位的进位的话,在当前位回溯之后,v的值依然没有改变。 那么,跟踪程序,我们发现,在前4种情况中,由于扩展了后一层就exit,回溯到该层后又exit,这样子是没有影响的。 但是如果是情况5,而且下一步进行的是当前层的下一个虫食数,那么当前层前一位的进位就被改变为当前层搜索到底端的左边的进位,也就是说,当前层的进位被回溯的子过程更改了,状态变了所以不能保证搜索到全部的解空间 当然,只设一个变量v(就是不定义为数组)也可以,但是必须在每个过程中设一个临时变量保存v的值,搜索回溯后再还原) 还有一种就是把v设置为函数的参数,三种都行,不过还是第一种好,因为无需回溯后再赋值(数组的长度就代表递归的层数)也无需传参} a: array[-30..30]ofchar;//反哈希,数字对应的字母 n,traceback,count: longint; st: array[1..3]ofstring;//所求的虫食数 procedureinit; varsr: string;i,j: longint; begin readln(n); fillchar(v,sizeof(v),0); fillchar(ct,sizeof(ct),false); fori: =-30to-1doct[i]: =true; fillchar(ans,sizeof(ans),$ff); traceback: =99999;fillchar(a,sizeof(a),$ff); fori: =1to3do begin readln(sr); st[i]: =''; forj: =1tondo st[i]: =sr[j]+st[i]; end; count: =0; end; procedureprint; varch: char; begin forch: ='A'tochr(n+63)dowrite(ans[ch],''); writeln(ans[chr(n+64)]); close(input);close(output);halt; end; functioncut(y: longint): longint; varki: longint; begin cut: =0; forki: =1to3do ifposi[st[ki,y]]>cutthencut: =posi[st[ki,y]]; end;//三行中每行第y列的字母所对应的posi值的最大值为cut函数的值 procedureotry(k,num: longint);//第k列,第n行(st[n]) //仔细跟踪可以发现,大部分过程中num都是1,所以是有可能将过程弄到只有一个变量 vari,i1,j,j1,t1,t2,t3,x,tx,temp: longint; begin inc(count); ifk>n then ifv[n]=0 thenprint elseexit; tx: =ans[st[1,k]]+ans[st[2,k]];
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- alpha