信奥赛CSP-J复赛集训(DP专题)(28):P2946 [USACO09MAR] Cow Frisbee Team S
信奥赛CSP-J复赛集训(DP专题)(28):P2946 [USACO09MAR] Cow Frisbee Team S
题目描述
老唐最近迷上了飞盘,约翰想和他一起玩,于是打算从他家的 N N N 头奶牛中选出一支队伍。
每只奶牛的能力为整数,第 i i i 头奶牛的能力为 R i R_i Ri。飞盘队的队员数量不能少于 1 1 1、大于 N N N。一支队伍的总能力就是所有队员能力的总和。
约翰比较迷信,他的幸运数字是 F F F,所以他要求队伍的总能力必须是 F F F 的倍数。请帮他算一下,符合这个要求的队伍组合有多少?由于这个数字很大,只要输出答案对 1 0 8 10^8 108 取模的值。
输入格式
第一行:两个用空格分开的整数: N N N 和 F F F。
第二行到 N + 1 N+1 N+1 行:第 i + 1 i+1 i+1 行有一个整数 R i R_i Ri,表示第 i i i 头奶牛的能力。
输出格式
第一行:单个整数,表示方案数对 1 0 8 10^8 108 取模的值。
输入输出样例 #1
输入 #1
4 5
1
2
8
2
输出 #1
3
说明/提示
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2000 1 \le N \le 2000 1≤N≤2000, 1 ≤ F ≤ 1000 1 \le F \le 1000 1≤F≤1000, 1 ≤ R i ≤ 1 0 5 1 \le R_i \le 10^5 1≤Ri≤105。
AC代码:
#include<bits/stdc++.h>
using namespace std;const int MOD = 1e8; // 定义模数
int n, f, a[2010]; // n为奶牛数,f为幸运数,a存储每头奶牛能力值模f后的余数
int dp[2010][1010]; // dp[i][j]表示前i头奶牛中选出若干头,总余数为j的方案数int main() {cin >> n >> f;for (int i = 1; i <= n; i++) {cin >> a[i];a[i] %= f; // 预处理,转换为模f的余数,便于后续计算}// 初始化:单独选第i头奶牛的情况,余数为a[i],方案数为1for (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++) { // 遍历所有可能的余数// 状态转移:当前状态由不选第i头、选第i头两种情况转移而来// 不选第i头:方案数继承自dp[i-1][j]// 选第i头:需要前i-1头的余数为(j - a[i] + f) % f,确保加上a[i]后余数为jdp[i][j] = (dp[i][j] + dp[i-1][j] + dp[i-1][(j - a[i] + f) % f]) % MOD;}}cout << dp[n][0] << endl; // 输出前n头奶牛中余数为0的方案数return 0;
}
功能分析
该程序使用动态规划解决组合问题,目标是找出所有可能的奶牛组合,使其总能力值为给定幸运数F的倍数。关键点在于利用模运算缩小状态规模,并通过动态规划递推求解。
- 预处理:将每头奶牛的能力值转换为模F的余数,简化后续计算。
- 初始化:每头奶牛单独成组的情况直接处理。
- 状态转移:对于每头奶牛,分两种情况(选或不选)更新状态,确保每次转移后余数正确。
- 结果输出:最终答案为所有奶牛处理完毕后余数为0的组合数,即满足条件的方案总数。
文末彩蛋:
关注并查看老师的个人主页,学习完整csp信奥赛完整系列课程: https://edu.csdn.net/lecturer/7901