HDU5456 Matches Puzzle Game[数位DP]


题意:

给T组数据,每组数组给一个n和m。n代表有n根小棍,问用这些小棍去组成一个A-B=C的等式有多少种方法,结果取模m。


题解:

先把题目所给的数字的摆法需要的个数,用数组存起来。

注意这个题并没有一个上界来让我们进行保存dfs过程中的值,所以只能进行每组数据的时候,重新将dp重置为-1,不然答案并不对。

根据等式,我们可以直接转换成A=B+C的形式,那么我们我们只需要枚举每一位的B和C进行加法运算,如果需要进位就标记一下。

这样我们一共有8种状态,换成二进制的形式

000 代表三个都没枚举完毕

001 代表C已经是确定的了

010 代表B已经是确定的了

011 代表BC都已经是确定的

100 代表A是确定的了(但是这种状态必须保证BC已存在,所以在计算过程中并不会单独出现)

101 同上 同时C已确定,但是这种状态不存在

110 同上 同时B已确定,但是这种状态不存在

111 代表三个都枚举出来了

dfs的时候,需要同时枚举出当前位的B和C,所以有个二重循环,用当前位BC的和计算出A的当前位,并标记是否有进位,如果B已存在,那么B只能出现数字0,如果C已存在,那么也只能出现数字0。

A成功枚举出来仅当当前枚举不出现进位,并且sum(当前BCA枚举出来的消耗)+3=剩余值,A才可成功,因为+3是指减号-号跟等号=的三根小棍。

状态转移的话,dp[i][j][k] 第一维表示剩余可用的小棍数,第二维表示是否需要进位,第三维表示当前枚举ABC的枚举状态,即上述。



#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<algorithm>
#include<queue>
#include<stack>
#include<set>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
const int N=1e3;
int n;
int use[]={6,2,5,5,4,5,6,3,7,6};
ll m;
ll dp[N][2][1<<3];
//001 c存在 010 b存在 100 a存在
ll dfs(int left,int up,int state)
{
    if (dp[left][up][state]!=-1)
        return dp[left][up][state];
    if (left<=0)
        return (!left && !up && state==7);
    ll cnt=0;
    for (int i=0 ; i<10 ; ++i)
    {
        if (state&2 && i)
                break;
        for (int j=0 ; j<10 ; ++j)
        {
            int b=i,c=j,a=(i+j+up)%10;
            if ((state&1 && c) || (state&2 && b))
                break;
            int sum=(state&1?0:use[c])+(state&2?0:use[b])+use[a];
            if (sum>left)
                continue;
            bool visit[8]={0};
            bool need=(i+j+up)>=10;
            for (int k=0 ; k<8 ; ++k)
            {
                bool flag=0;
                int t=0;
                if (k&1 && c)
                    t|=1;
                if (k&2 && b)
                    t|=2;
                if (k&4 && a && !need && left==sum+3)
                {
                    t|=4;
                    flag=1;
                }
                if (!visit[t])
                {
                    if (flag)
                        cnt=(cnt+dfs(left-sum-3,need,state|t))%m;
                    else
                        cnt=(cnt+dfs(left-sum,need,state|t))%m;
                    visit[t]=1;
                }
            }
        }
    }
    return dp[left][up][state]=cnt;
}
int main()
{
	int T;
	scanf("%d",&T);
	for (int test=1 ; test<=T ; ++test)
    {
        scanf("%d%lld",&n,&m);
        memset(dp,-1,sizeof(dp));
        printf("Case #%d: %lld\n",test,dfs(n,0,0));
    }
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值