Clarke and problem HDU - 5464(dp 类似背包转移)

题目链接

题意

  1. 给我们 n 个数的,我们可以从中选择任意个数把这些数的值加起来,要求这个数是 m 的倍数,问我们组成的数字是的倍数是 m 的方案数,
  2. 注意一个也不选组成的数是 0 也算一种合法方案。
  3. n <=1000, p<=1000.

思路

  1. 一看到组成的数是 m 的倍数就要明白我们可以在 dp 的状态中用一位 去枚举余数,
  2. 其实 dp 方程很容易就可以猜出来:dp [i][j] 表示前 i 个数从中选择一些数字的和是 % m 的余数是 j 的方案数,那么答案就是 dp [n][0] 了 ,
  3. 那么状态转移方程为:
    1. 如果第 i 个数我们不选择:dp [i][j] += dp [i - 1][j]
    2. 如果选择第 i 个数:dp [i][(j + a [i]) % m] += dp [i][j]

代码

#include <bits/stdc++.h>
using namespace std;
#define db  double
#define ll  long long
#define sc  scanf
#define pr  printf
#define fi  first
#define se  second
#define pb  push_back
#define m_p make_pair
#define Pir pair<int, int>
#define inf 0x3f3f3f3f
#define INF 0x3f3f3f3f3f3f3f3f
/*==========ACMer===========*/
const int N = 1005;
const int mod = 1e9 + 7;
int n, p, a[N];
int dp[N][N];


int main()
{
    int T; sc("%d", &T);
    while (T --)
    {
        sc("%d %d", &n, &p);
        for (int i = 1; i <= n; i ++) sc("%d", &a[i]);
        memset(dp, 0, sizeof dp);
        dp[0][0] = 1;

        for (int i = 1; i <= n; i ++)
        {
            for (int j = 0; j < p; j ++)
            {
                if (dp[i - 1][j] == 0) continue;
                int k = (j + a[i] % p + p) % p;
                dp[i][k] = (dp[i][k] + dp[i - 1][j]) % mod;
                dp[i][j] = (dp[i][j] + dp[i - 1][j]) % mod; 
            }
        }
        pr("%d\n", dp[n][0]);
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值