Matches Puzzle Game
Problem DescriptionAs an exciting puzzle game for kids and girlfriends, the Matches Puzzle Game asks the player to find the number of possible equations A−B=C with exactly n (5≤n≤500) matches (or sticks).
In these equations, A,B and C are positive integers. The equality sign needs two matches and the sign of subtraction needs just one. Leading zeros are not allowed.
Please answer the number, modulo a given integer m (3≤m≤2×109).InputThe input contains several test cases. The first line of the input is a single integer t which is the number of test cases. Then t (1≤t≤30) test cases follow.
Each test case contains one line with two integers n (5≤n≤500) and m (3≤m≤2×109).OutputFor each test case, you should output the answer modulo m.Sample Input4 12 1000000007 17 1000000007 20 1000000007 147 1000000007Sample OutputCase #1: 1 Case #2: 5 Case #3: 38 Case #4: 815630825Source
【题意】
有n根火柴(n<=500),要刚好用完,摆出一个减式A-B=C(A>0,B>0,C>0)求有多少种方案
【分析】
这题很好想,但是就看个人DP能力了,我一开始打的DP就TLE,真是太年轻!
之前做的几题数位DP都是数字限制,即数值固定一个区间,而现在这题可不管你那个数有多大,只要火柴够用就好了。
所以之前的题我是记录位数,标记前导0的,再加个越限标记flag。
搞到我这题一开始就也记录位数了,然记录位数并无卵用!!还会TLE啊ORZ,
我一开始想法,先减掉3根火柴,减法变加法(加法好打一些),f[i][j][k]表示i位,j根火柴,k表示状态(即两个加数分别是否还处于前导0中),然后记忆化搜索
位数不超过n/4的,所以时间大概是150*500*4*10*10*2,代码也放一下,正确性还是保证的:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 #include<cmath> 8 using namespace std; 9 #define LL long long 10 11 int f[150][510][4][2];//weishu huocai zero shifoujinwei 12 // 0 00 1 0x 2 x0 3 xx 13 int us[10]={6,2,5,5,4,5,6,3,7,6}; 14 int m,sum; 15 16 int ffind(int n,int k,int zero,int step) 17 { 18 sum++; 19 if(k<3) return 0; 20 if(n==0) return (k==3&&step==0); 21 if(f[n][k][zero][step]!=-1) return f[n][k][zero][step]; 22 LL ans=0; 23 for(int a=0;a<10;a++) 24 for(int b=0;b<10;b++) 25 { 26 for(int l=0;l<2;l++) //xia yi bu shi fou jin wei 27 { 28 if(n==1&&a==0&&zero<=1) continue; 29 if(n==1&&b==0&&zero!=1&&zero!=3) continue; 30 if(step&&(a+b+l<10)) continue; 31 if(!step&&(a+b+l>=10)) continue; 32 int now=0; 33 if(a!=0||zero==2||zero==3) now+=us[a]; 34 if(b!=0||zero==1||zero==3) now+=us[b]; 35 if(a!=0||b!=0||l!=0||zero!=0) now+=us[(a+b+l)%10]; 36 if(now>k) continue; 37 38 int nz; 39 if(a==0&&b==0&&zero==0) nz=0; 40 else if(a==0&&zero!=2&&zero!=3) nz=1; 41 else if(b==0&&zero!=1&&zero!=3) nz=2; 42 else nz=3; 43 ans=(ans+ffind(n-1,k-now,nz,l) )%m; 44 } 45 } 46 f[n][k][zero][step]=(int)ans; 47 return (int)ans; 48 } 49 50 int main() 51 { 52 int T,kase=0; 53 scanf("%d",&T); 54 while(T--) 55 { 56 sum=0; 57 int n; 58 scanf("%d%d",&n,&m); 59 memset(f,-1,sizeof(f)); 60 printf("Case #%d: %d ",++kase,ffind(n/4,n,0,0)); 61 } 62 return 0; 63 }
其实与位数无关,但是不及记录位数就要从低位开始填数了,不然无法表示,会重复。
f[j][k]表示j根火柴,k状态,状态表示当前两个加数分别是否结束了,结束了只能填0,并且不花费火柴。
AC代码如下:
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 #include<cmath> 8 using namespace std; 9 #define LL long long 10 11 int f[510][4][2];//weishu huocai zero shifoujinwei 12 // 0 00 1 0x 2 x0 3 xx 13 int us[10]={6,2,5,5,4,5,6,3,7,6}; 14 int m; 15 16 int ffind(int k,int zero,int step) 17 { 18 if(k<3) return 0; 19 if(zero==0) 20 { 21 if(step==1) k-=2; 22 return k==3; 23 } 24 if(f[k][zero][step]!=-1) return f[k][zero][step]; 25 LL ans=0; 26 for(int a=0;a<10;a++) 27 { 28 for(int b=0;b<10;b++) 29 { 30 int now=0; 31 if(zero==2||zero==3) now+=us[a]; 32 if(zero==1||zero==3) now+=us[b]; 33 now+=us[(a+b+step)%10]; 34 if(now>k) continue; 35 36 ans=(ans+ffind(k-now,zero,a+b+step>=10) )%m; 37 if(a!=0&&zero!=0&&zero!=1) ans=(ans+ffind(k-now,zero==3?1:0,a+b+step>=10) )%m; 38 if(b!=0&&zero!=0&&zero!=2) ans=(ans+ffind(k-now,zero==3?2:0,a+b+step>=10) )%m; 39 if(a!=0&&b!=0&&zero==3) ans=(ans+ffind(k-now,0,a+b+step>=10) )%m; 40 41 if(zero==0||zero==2) break; 42 } 43 if(zero==0||zero==1) break; 44 } 45 46 f[k][zero][step]=(int)ans; 47 return (int)ans; 48 } 49 50 int main() 51 { 52 int T,kase=0; 53 scanf("%d",&T); 54 while(T--) 55 { 56 int n; 57 scanf("%d%d",&n,&m); 58 memset(f,-1,sizeof(f)); 59 printf("Case #%d: %d ",++kase,ffind(n,3,0)); 60 } 61 return 0; 62 }
所以如果跟位数无关,最好思想回到原始的位置啊,从低位开始填可能会--柳暗花明又一村??
2016-10-09 14:15:15