hduacm 5464 Clarke and problem 题解

题目连接
http://acm.hdu.edu.cn/showproblem.php?pid=5464

题目分析:
比较典型的递推型dp,有经验的人可以马上判断出使用dp做题。题目给我们一系列数字,让我们判断得到某个数的倍数的组合数,一想到倍数,仅仅让我们判断的是倍数而非其他,那么我们应该联想到取模,取模可以为我们缩减数据的大小,而且,只要模为0就是倍数。

其次,数据类型可正可负,比如我们得到的p是5,那么将数据经历一次对5取模后,留下的都是在【-4,4】内的数字,负数是有点头疼的东西,一个纯粹由自然数组成的代数系统更利于我们的计算,那么怎么办,回想一下在这个加取模运算形成的代数系统中的运算关系,对于这里的加取模运算,5+(-2)与5+3是是相等的,因为3%5==8%5,对于这个运算来说,-1与4,-2与3,-3与2,-4与1为等价,所以对于模5运算来说,任何数+4与-1是相同的,(n+4)%5==(n-1)%5,别的情况以此类推。
所以对于任何取模后为负的数字加上一个p,我们最终得到的数字为[0,p-1]区间内的数字。

递推
这个比较好解决,比如我们有8个数,p依然为5,我们已经知道了前4个数可以组成和取模后的所有的情况的数量,那么在知道第5个数的情况下,就可以推出前5个数能组成的所有情况,比如前4个数在处理后为2,0,3,1 可以得到
模为1的情况为{{1},{1,0},{2,3},{2,3,0}} 4种
模为2的情况为{{2},{2,0},{2,3,1},{2,3,1,0}} 4种
模为3的情况为{{3},{3,0},{1,2},{1,2,0}}4种
模为4的情况为{{3,1},{3,1,0}}2种
模为0的情况(倍数情况){∅,{0},{3,2},{3,2,0}}四种(什么都不取算0)
然后假设第5个数为3
那么前5数的情况中模为1就是 上面的情况中1与3的总和,别的依次类推。

最后一个要点就是MOD 1e9+7的问题,这个就在每次更新数据时求一下MOD,不会对最终答案造成影响,因为任何时候任何情况下的数量都可以写成N+p*MOD,而最终答案其实也就是FN+P1*MOD+P2*MOD+。。。任何时候取模都不会对FinalAnswer造成影响。

最后上代码

#include <iostream>
#include <string.h>
#define MOD (1000000000+7)
long long dp[1001][1001];
int N[1001];

int main()
{
    int T;
    int n, p;
    scanf("%d", &T);

    while (T--)
    {
        scanf("%d%d", &n, &p);
        memset(dp, 0, sizeof(dp));
        for (int i = 1; i <= n;++i)
        {
            scanf("%d", &N[i]);
            N[i] %= p;
            if (N[i] < 0) N[i] += p;
        }

        dp[0][0] = 1;
        for (int i = 1; i <= n; i++)
        for (int k = 0; k < p; k++)
        {
            dp[i][k] = (dp[i - 1][k] + dp[i - 1][((k - N[i]) + p) % p]) % MOD;
        }

        printf("%I64d\n", dp[n][0]);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值