zoukankan      html  css  js  c++  java
  • 【HDU 5456】 Matches Puzzle Game (数位DP)

    Matches Puzzle Game



    Problem Description
    As an exciting puzzle game for kids and girlfriends, the Matches Puzzle Game asks the player to find the number of possible equations AB=C with exactly n (5n500) 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 (3m2×109).
    Input
    The 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 (1t30) test cases follow.

    Each test case contains one line with two integers n (5n500) and m (3m2×109).
    Output
    For each test case, you should output the answer modulo m.
    Sample Input
    4 12 1000000007 17 1000000007 20 1000000007 147 1000000007
    Sample Output
    Case #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
    ",++kase,ffind(n/4,n,0,0));
    61     }
    62     return 0;
    63 }
    TLE的代码

      其实与位数无关,但是不及记录位数就要从低位开始填数了,不然无法表示,会重复。

        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 }
    [HDU 5456]

    所以如果跟位数无关,最好思想回到原始的位置啊,从低位开始填可能会--柳暗花明又一村??

    2016-10-09 14:15:15

  • 相关阅读:
    1.认识移动端 、前端工作流程 2019-2-13
    去掉标签默认样式属性 + visibility
    grid 布局:一般用于多行排版、单页排版、......(响应式布局)
    解决 display 和 transition 冲突的问题
    回到顶部效果
    文字溢出 生成 省略号
    【Python】协程实现生产者消费者模型
    【Python】0/1背包、动态规划
    【Python】使用super初始化超类
    【Python】考虑用生成器改写直接返回列表的函数
  • 原文地址:https://www.cnblogs.com/Konjakmoyu/p/5942038.html
Copyright © 2011-2022 走看看