HDU - 5456 Matches Puzzle Game (数位dp)

题目链接

题意:给你n根火柴棒(5<=n<=500),问这些火柴棒能拼成多少种形如A-B=C的等式(不包含前导0)。

HRBUST - 1118的强化版,那个直接搜索就能过,这个只能dp。

分析:首先去掉3根火柴棒来凑成减号和等号,然后问题就变成了将剩下的火柴棒拼成B+C=A的形式。

我们观察能凑成的等式中三个数字的相同位置,发现无非就以下几种情况:

(1)B\: \: C\: \: (B+C)\%10,e.p.,1\: \: 2\: \: 3\: \: ,8\: \: 6\: \: 4\:

(2)B\: \: C\: \: (B+C+1)\%10,e.p.,2\: \: 3\: \: 6\: \: ,7\: \: 5\: \: 3\:

(3)B\: \: \setminus\: \: B,e.p.,4\: \: \setminus\: \: 4\: (\setminus:blank)

(4)\setminus\: \: C\: \: C,e.p.,\setminus\: \: 9\: \: 9

(5)B\: \: \setminus\: \: (B+1)\%10,e.p.,2\: \: \setminus\: \: 3\: \: ,\: \: 9\: \: \setminus\: \: 0

(6)\setminus\: \: C\: \: (C+1)\%10,e.p.,\setminus\: \: 6\: \: 7\: \: ,\: \: \setminus\: \: 9\: \:0

(7)\setminus\: \: \setminus\: \: 1\: (end)

其中,从低位向高位转移的可行方案有:

(1)(2)->(1)(3)(4)(不进位),(2)(5)(6)(7)(进位)

(3)->(3)

(4)->(4)

(5)->(3)(不进位),(5)(7)(进位)

(6)->(4)(不进位),(6)(7)(进位)

看上去似乎有点复杂。为了直观,我们用d[n][a][b][c]来表示剩余n根火柴棒时,从最低位开始的放法总数,a表示最低位是否因进位需要+1,b表示是否已停止放B位,c表示是否已停止放C位,则最终我们所求的结果就是d[n-3][0][0][0]。

#define FRER() freopen("i.txt","r",stdin)
#define FREW() freopen("o.txt","w",stdout)

#include
using namespace std;
typedef long long ll;
const int N=500+10;
const int num[]= {6,2,5,5,4,5,6,3,7,6};

int n,kase=0;
ll d[N][2][2][2],mod;
ll dp(int cnt,int a,int b,int c)
{
    if(cnt<0)return 0;
    if(~d[cnt][a][b][c])return d[cnt][a][b][c];
    if(b&&c)
    {
        if((a&&cnt==num[1])||(!a&&!cnt))return 1;
        else return 0;
    }
    d[cnt][a][b][c]=0;
    if(!b&&!c)
    {
        for(int i=0; i<=9; ++i)
            for(int j=0; j<=9; ++j)
            {
                d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,0,0);
                if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,1,0);
                if(j)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,0,1);
                if(i&&j)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[j]+num[(i+j+a)%10]),(i+j+a)/10,1,1);
            }
    }
    else if(!b)
    {
        for(int i=0; i<=9; ++i)
        {
            d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,0,1);
            if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,1);
        }
    }
    else if(!c)
    {
        for(int i=0; i<=9; ++i)
        {
            d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,0);
            if(i)d[cnt][a][b][c]+=dp(cnt-(num[i]+num[(i+a)%10]),(i+a)/10,1,1);
        }
    }
    return d[cnt][a][b][c]%=mod;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(d,-1,sizeof d);
        scanf("%d%I64d",&n,&mod);
        printf("Case #%d: %I64d\n",++kase,dp(n-3,0,0,0));
    }
    return 0;
}

 

你可能感兴趣的:(dp)