【题解】 [USACO 2009 Mar] Cow Frisbee Team S
题目描述
题目大意
从 \(N\) 个整数中取若干个(不能不选),且取的数之和为 \(F\) 的倍数的总方案数对\(10^8\)取余的值。
思路
这道题是一道二维线性DP。那么根据线性DP的解题方法。
首先,找出题目的阶段性。这道题显而易见方案数随着选择的个数而变化,那么这道题就以选择的个数作为阶段。
接着,根据阶段定义状态。这道题的状态可以定义为 \(dp_{i, j}\) 表示 从前 \(i\) 个数中取,且取的数之和对 \(F\) 取余的值为 \(j\) 的 方案数对\(10^8\)求余的值。
然后,根据状态分析状态转移方程。每一个状态的情况可以分类讨论:
- 不取第 \(i\) 个数,则从前 \(i - 1\) 个数中取的数之和对 \(F\) 取余的值亦要为 \(j\),那么不取第 \(i\) 个数,余数仍为 \(j + 0 = j\)。
- 不取第 \(i\) 个数,则从前 \(i - 1\) 个数中取的数之和对 \(F\) 取余的值要为 \((j - a_i + F) \mod F\),那么取第 \(i\) 个数,余数变为 \((j - a_i + F) \mod F + a_i = j\)。
综上所述,状态转移方程为:$$dp_{i, j} = (dp_{i, j} + dp_{i - 1, j} + dp_{i - 1, (j - a_i + F) \mod F}) \mod 10^8$$
最后,定义初值。即 $$dp_{i, a_i \mod F} = 1$$
最终输出的是从前 \(n\) 个数中取,且取的数之和对 \(F\) 取余的值为 \(0\) 的总方案数对\(10^8\)取余的值,即 \(dp_{n, 0}\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2005, F = 1005, MOD = 1e8;
int n, f, a[N];
int dp[N][F];
int main()
{
scanf("%d%d", &n, &f);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &a[i]);
a[i] %= f; // 预处理取模
}
// 定义初值
for (int i = 1; i <= n; i ++ ) dp[i][a[i]] = 1;
// 状态转移
for (int i = 1; i <= n; i ++ )
for (int j = 0; j < f; j ++ )
dp[i][j] = (dp[i][j] + dp[i - 1][j] + dp[i - 1][(j - a[i] + f) % f]) % MOD;
printf("%d\n", dp[n][0]); // 输出答案
return 0;
}