题目描述
管道取珠 【问题描述】 管道取珠是小X很喜欢的一款游戏。在本题中,我们将考虑该游戏的一个简单改版。游戏画面如图1所示: (图1) 游戏初始时,左侧上下两个管道分别有一定数量的小球(有深色球和浅色球两种类型),而右侧输出管道为空。每一次操作,可以从左侧选择一个管道,并将该管道中最右侧的球推入右边输出管道。 例如,我们首先从下管道中移一个球到输出管道中,将得到图2所示的情况。 (图2) 假设上管道中有n个球, 下管道中有m个球,则整个游戏过程需要进行n + m次操作,即将所有左侧管道中的球移入输出管道。最终n + m个球在输出管道中从右到左形成输出序列。 爱好数学的小X知道,他共有C(n+m, n)种不同的操作方式,而不同的操作方式可能导致相同的输出序列。举个例子,对于图3所示的游戏情形: (图3) 我们用A表示浅色球,B表示深色球。并设移动上管道右侧球的操作为U, 移动下管道右侧球的操作为D,则共有C(2+1,1)=3种不同的操作方式, 分别为UUD, UDU, DUU;最终在输出管道中形成的输出序列(从右到左)分别为BAB,BBA,BBA。可以发现后两种操作方式将得到同样的输出序列。 假设最终可能产生的不同种类的输出序列共有K种,其中第i种输出序列的产生方式(即不同的操作方式数目)有ai个。聪明的小X早已知道,(图4) 因此,小X希望计算得到(图5) 你能帮助他计算这个值么?由于这个值可能很大,因此只需要输出该值对1024523的取模即可(即除以1024523的余数)。 说明:文中C(n + m, n)表示组合数。组合数C(a, b)等价于在a个不同的物品中选取b个的选取方案数。 【输入文件】 输入文件ball.in第一行包含两个整数n, m,分别表示上下两个管道中球的数目。 第二行为一个AB字符串,长度为n,表示上管道中从左到右球的类型。其中A表示浅色球,B表示深色球。 第三行为一个AB字符串,长度为m,表示下管道中的情形。 【输出文件】 输出文件ball.out仅包含一行,即为图5除以1024523的余数。 【输入样例】 2 1 AB B 【输出样例】 5 【样例说明】 样例即为文中(图3)。共有两种不同的输出序列形式,序列BAB有1种产生方式,而序列BBA有2种产生方式,因此答案为5。 【大致数据规模】 约30%的数据满足 n, m ≤ 12; 约100%的数据满足n, m ≤ 500。
图1:
图2:
图3:
图4:
图5:
解法1:搜索
1 program ball; 2 const std=1024523; 3 maxf=100000; 4 type 5 ty1=^ty2; 6 ty2=record 7 s,num:int64; 8 next:ty1; 9 end; 10 11 var 12 n,m,i:longint; 13 ans:int64; 14 pi:char; 15 p:ty1; 16 inn:array[0..2,0..500] of longint; 17 first:array[0..maxf] of ty1; 18 //=========================== 19 procedure insert(s:int64); 20 var 21 k:longint; 22 p:ty1; 23 begin 24 k:=s mod maxf; 25 p:=first[k]; 26 while p<>nil do 27 begin 28 if p^.s=s then 29 begin 30 inc(p^.num); 31 exit; 32 end; 33 p:=p^.next; 34 end; 35 new(p); 36 p^.s:=s; 37 p^.num:=1; 38 p^.next:=first[k]; 39 first[k]:=p; 40 end; 41 //=========================== 42 procedure find(nt,mt,k:longint; s:int64); 43 begin 44 if k=0 then 45 begin 46 insert(s); 47 exit; 48 end; 49 if nt=0 then find(nt,mt-1,k-1,s or (inn[2,mt]<<(k-1))) else 50 if mt=0 then find(nt-1,mt,k-1,s or (inn[1,nt]<<(k-1))) else 51 begin 52 find(nt,mt-1,k-1,s or (inn[2,mt]<<(k-1))); 53 find(nt-1,mt,k-1,s or (inn[1,nt]<<(k-1))); 54 end; 55 end; 56 //=========================== 57 begin 58 assign(input,'ball.in'); reset(input); 59 assign(output,'ball.out'); rewrite(output); 60 readln(n,m); 61 for i:=1 to n do 62 begin 63 read(pi); 64 if pi='B' then inn[1,i]:=1; 65 end; 66 readln; 67 for i:=1 to m do 68 begin 69 read(pi); 70 if pi='B' then inn[2,i]:=1; 71 end; 72 find(n,m,n+m,0); 73 for i:=0 to maxf do 74 begin 75 p:=first[i]; 76 while p<>nil do 77 begin 78 inc(ans,sqr(p^.num) mod std); 79 if ans>=std then dec(ans,std); 80 p:=p^.next; 81 end; 82 end; 83 writeln(ans); 84 close(input); close(output); 85 end.
解法2:动态规划
1 program ball; 2 uses math; 3 const std=1024523; 4 maxn=503; 5 var 6 n,m,i1,j1,i2,j2,val:longint; 7 f:array[0..maxn,0..maxn,0..maxn] of longint; 8 u,v:array[0..maxn] of char; 9 //======================== 10 procedure add(var a:longint; b:longint); inline; 11 begin 12 inc(a,b); 13 if a>=std then dec(a,std); 14 end; 15 //======================== 16 begin 17 assign(input,'ball.in'); reset(input); 18 assign(output,'ball.out'); rewrite(output); 19 readln(n,m); 20 for i1:=1 to n do read(u[i1]); readln; 21 for i2:=1 to m do read(v[i2]); readln; 22 f[0,0,0]:=1; 23 for i1:=0 to n do 24 for j1:=0 to m do 25 for i2:=0 to n do 26 begin 27 j2:=i1+j1-i2; val:=f[i1,j1,i2]; 28 if val=0 then continue; //这个优化很重要!——重视细节 29 if u[i1+1]=u[i2+1] then add(f[i1+1,j1,i2+1],val); 30 if u[i1+1]=v[j2+1] then add(f[i1+1,j1,i2],val); 31 if v[j1+1]=u[i2+1] then add(f[i1,j1+1,i2+1],val); 32 if v[j1+1]=v[j2+1] then add(f[i1,j1+1,i2],val); 33 end; 34 writeln(f[n,m,n]); 35 close(input); close(output); 36 end.