Cow Frisbee Team S(变相的01背包)

题目描述
老唐最近迷上了飞盘,约翰想和他一起玩,于是打算从他家的 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

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值