【HDU 5456】 Matches Puzzle Game (数位DP)
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 1000000007
Sample OutputCase #1: 1 Case #2: 5 Case #3: 38 Case #4: 815630825
Source
【题意】
有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\n",++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\n",++kase,ffind(n,3,0)); 60 } 61 return 0; 62 }
所以如果跟位数无关,最好思想回到原始的位置啊,从低位开始填可能会--柳暗花明又一村??
2016-10-09 14:15:15