题目描述
老唐最近迷上了飞盘,约翰想和他一起玩,于是打算从他家的 NN 头奶牛中选出一支队伍。
每只奶牛的能力为整数,第 i 头奶牛的能力为R_i 。飞盘队的队员数量不能少于 1、大于N。一支队伍的总能力就是所有队员能力的总和。
约翰比较迷信,他的幸运数字是 F ,所以他要求队伍的总能力必须是 F 的倍数。请帮他
算一下,符合这个要求的队伍组合有多少?由于这个数字很大,只要输出答案对 108取模的值。
输入格式
第一行:两个用空格分开的整数:N和 F。
第二行到 N+1 行:第i+1 行有一个整数R_i,表示第 i头奶牛的能力。
输出格式
加粗样式第一行:单个整数,表示方案数对 108 取模的值。
输入输出样例
输入
4 5
1
2
8
2
输出
3
说明/提示
数据范围与约定
对于 100%100% 的数据,1≤N≤2000,1≤F≤1000 ,1≤R_i≤105
在看完题目之后,其实就是01背包的思想,每头牛只有两个状态,取或者是不取,但它又跟01背包不太一样,经典的01背包是取价值的最大值,但是这个题目时取模上F为0的值,这就难到了不少刚接触01背包的小伙伴,其实这道题虽然有01背包的思想,其实是一道DP动态规划的题目。
经典的01背包问题是只用一维DP即可解决问题,但是该题目有了一个新的要求,值模上幸运数字F为几。
那么就可以用二维DP,第一维表示考虑前多少头奶牛,第二维DP用来表示所选的奶牛的权值和模上F之后为几
这点如果能想到,那就变得非常简单了,最后结果只需要输出遍历完所有奶牛之后模为0的DP即可,即dp[n][0]
#include <iostream>
using namespace std;
#define mod 100000000
int dp[2055][2055],r[2055];
int main()
{
int n,f;
cin>>n>>f;
for(int i=1;i<=n;i++){
cin>>r[i];
r[i]%=f;//输入时就进行模F的操作,便于后续计算,减小数值
}
for(int i=1;i<=n;i++)
dp[i][r[i]]=1;//初始化dp,只考虑第i头牛
for(int i=1;i<=n;i++){
for(int j=0;j<=f-1;j++){
dp[i][j]=((dp[i][j]+dp[i-1][j])%mod+dp[i-1][(j-r[i]+f)%f]%mod)%mod;
//算出所有模上f为j的情况共有三个值
//1.dp[i][j]本身,表示只取第i头牛
//2.dp[i-1][j],表示不选第i头奶牛,前i-1头奶牛中模f为j的情况数
//3.dp[i-1][(j-r[i]+f)%f]表示选第i头牛,第i头牛的值为r[i],再加上考虑到前i-1头牛时,值为j-r[i]的情况
//但是j-r[i]可能小于0,所以需要加上f再模上f
}
}
cout<<dp[n][0];
return 0;
}
取或不取,思想跟01背包的思想一样,但是需要对01背包进行变形,变成了DP,我感觉01背包的思想服务于DP动态规划,该题因该算一道经典DP题目 。背包属于DP