Ant Counting多重集组合数

本文深入探讨了AntCounting问题的算法解决方案,该问题旨在计算特定范围内蚂蚁家族的不同组队方式数量。通过动态规划方法,文章详细解释了如何避免重复计算并高效求解。适用于对算法设计与分析感兴趣的读者。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Ant Counting

题目描述: 有一天,贝茜无聊地坐在蚂蚁洞前看蚂蚁们进进出出地搬运食物.很快贝茜发现有些蚂蚁长得几乎一模一样,于是她认为那些蚂蚁是兄弟,也就是说它们是同一个家族里的成员.她也发现整个蚂蚁群里有时只有一只出来觅食,有时是几只,有时干脆整个蚁群一起出来.这样一来,蚂蚁们出行觅食时的组队方案就有很多种.作为一头有数学头脑的奶牛,贝茜注意到整个蚂蚁群由T(1≤T≤1000)个家族组成,她将这些家族按1到T依次编号.编号为i的家族里有Ni(1≤Ni≤100)只蚂蚁.同一个家族里的蚂蚁可以认为是完全相同的.
如果一共有S,S+1….,B(1≤S≤B≤A)只蚂蚁一起出去觅食,它们一共能组成多少种不同的队伍呢?注意:只要两支队伍中所包含某个家族的蚂蚁数不同,我们就认为这两支队伍不同.由于贝茜无法分辨出同一家族的蚂蚁,所以当两支队伍中所包含的所有家族的蚂蚁数都相同时,即使有某个家族换了几只蚂蚁出来,贝茜也会因为看不出不同而把它们认为是同一支队伍.
3 sets with 1 ant: {1} {2} {3}
5 sets with 2 ants: {1,1} {1,2} {1,3} {2,2} {2,3}
5 sets with 3 ants: {1,1,2} {1,1,3} {1,2,2} {1,2,3} {2,2,3}
3 sets with 4 ants: {1,2,2,3} {1,1,2,2} {1,1,2,3}
1 set with 5 ants: {1,1,2,2,3}
*第1行:可以创建的大小为S…B(包括)的集合数。 像{1,2}这样的集合与集合{2,1}相同,不应重复计算。 仅打印此数字的最后六位数,不带前导零或空格。

思路分析:
我们先分析核心代码的来处:
f[i][j]=从前i种物品中选取j个物品的组合总数。
为了从前i种物品中选取j个,我们可以先从前i-1种物品中选取j-k个,然后在从第i个物品中补上剩下的k个
所以可以得到以下递推关系: 在这里插入图片描述
当j>a[i]时(j-1-a[i]>=0):
f[i][j]=f[i−1][j]+f[i−1][j−1]+…+f[i−1][j−ai]f[i][j]=f[i−1][j]+f[i−1][j−1]+…+f[i−1][j−ai] ,;
我们求 f[i][j] 的时候可以使用 f[i][j−1] 计算过的值,避免重复枚举 k.
f[i][j−1]=f[i−1][j−1]+…+f[i−1][j−ai−1];
根据上个式子我们可以得到:
f[i][j]=f[i][j−1]+f[i−1][j]−f[i−1][j−ai−1];

那么我们该怎么理解上述的式子呢?
1.不选选第i种:f[i-1][j];就是在前i-1种里面选择j个;
2.选择第i种 :f[i][j-1]-f[i-1][j-a[i]-1];
我们可以这样理解选择的情况:f[i][j-1]我们可以认为是选择了第i种1个,至于后面我们还选不选第i种我们先不管。f[i][j-1]是在前i种选择j-1个可组成的种数,那么它包含了选a[i]个第i种的情况,即f[i-1][j-a[i]-1],但是f[i][j]最多也就是拿x[i]项,所以要在这里减去f[i-1][j-a[i]-1];

当j<=a[i]时:

f[i][j]=f[i−1][j]+f[i−1][j−1]+…+f[i−1][0];
f[i][j-1] = f[i−1][j−1]+…+f[i−1][0];
由上面的两个式子可知:
f[i][j] = f[i-1][j]+f[i][j-1];
此处分析和上面的差不多,只是不可能存在第i种都取的情况;

#include<cstdio>
#include <iostream>
#include <algorithm>

using namespace std;
#define Maxn 1000
#define Maxm 100
#define Mod 1000000
int num[Maxn + 5], f[Maxn + 5][Maxn * Maxm + 5];

int main() {
    int t, a, s, b, ans = 0;
    scanf("%d %d %d %d", &t, &a, &s, &b);
    for (int i = 1; i <= a; i++) {          //存储每种的数量
        int x;
        scanf("%d", &x);
        num[x]++;
    }
    for (int i = 0; i <= t; i++)
        f[i][0] = 1;   //取0都会是一种
    for (int i = 1; i <= a; i++) {
        for (int j = 1; j <= t; j++) {
            if (j - 1 - num[i] >= 0)
                f[i][j] = (f[i - 1][j] + f[i][j - 1] - f[i-1][j - 1 - num[i]] + Mod) % Mod;
            else
                f[i][j] = (f[i - 1][j] + f[i][j - 1])%Mod;
        }
    }
    for (int i = s; i <= b; i++){
        ans += f[t][i];
        ans%=Mod;
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值