题意 : 有 n 种蚂蚁,第 i 种蚂蚁有ai个,一共有 A 个蚂蚁。不同类别的蚂蚁可以相互区分,但同种类别的蚂蚁不能相互区别。从这些蚂蚁中分别取出S,S+1...B个,一共有多少种取法。

分析 : 

实际就是要解决 => 从 n 种物品中取出 m 个有多少种取法 ( 同种无法区分 )

计数问题的 DP 定义必须保证不重复计数

这里定义 dp[i+1][j] => 从前 i 种物品中取出 j 个的组合数

根据定义为了从前 i 种物品中取出 j 个,可以从前 i-1 中取出 j-k 个并从 i 种中取出 k 个

即 dp[i+1][j] = ∑dp[i][j-k] 【 0 ≤ k ≤ min(j, ant[i]) 】

但是这个递推式的求和太耗时间,实际可以优化,考虑两种情况 j ≤ ant[i] 和 j > ant[i]

① j ≤ ant[i] ( 即 j-1 < ant[i] )

此时 ∑ 的上界 min( j, ant[i] ) = j ,将式子展开有 dp[i][0]+dp[i][1]+dp[i][2]...dp[i][j] ( k 从大到小枚举 )

将展开式的 dp[i][j] 取出来那么将得到 ∑dp[i][j-1-k] 【 0 ≤ k ≤ j-1 】( 其实这个就是 dp[i+1][j-1] !!! )

那么最后 dp[i+1][j] = dp[i+1][j-1] + dp[i][j]

② j > ant[i]

此时 ∑ 的上界 min( j, ant[i] ) = ant[i],将式子展开有 dp[i][j-ant[i]]+dp[i][j-ant[i]+1]...dp[i][j]

对比 ① 的结果,很明显如果用 ① 的结果 - dp[i][j-ant[i]-1] 就能得到上面的展开式了!

所以 ② 的情况下,dp[i+1][j] = ( dp[i+1][j-1] + dp[i][j] ) - dp[i][j-ant[i]-1]

#include<stdio.h>
#include<string.h>
using namespace std;
;
][];
];
int main(void)
{
    int T, A, S, B;
    while(~scanf("%d %d %d %d", &T, &A, &S, &B)){

        memset(num, , sizeof(num));
        ; i<=A; i++)
            scanf("%d", &temp),
            num[temp-]++;

        ; i<=T; i++)
            dp[i][] = ;

        ; i<T; i++){
            ; j<=B; j++){
                 - num[i] >= )
                    dp[i+][j] = (dp[i+][j-] + dp[i][j] - dp[i][j--num[i]] + mod)%mod;
                else
                    dp[i+][j] = (dp[i+][j-] + dp[i][j])%mod;
            }
        }

        ;
        for(int i=S; i<=B; i++)
            ans = (ans + dp[T][i])%mod;
        printf("%d\n", ans);
    }
    ;
}

其实 DP 的阶段 ( 数组第一维 ) 只跟前一个有关系,故用滚动数组优化

#include<stdio.h>
#include<string.h>
using namespace std;
;
][];
];
int main(void)
{
    int T, A, S, B;
    while(~scanf("%d %d %d %d", &T, &A, &S, &B)){

        memset(num, , sizeof(num));
        ; i<=A; i++)
            scanf("%d", &temp),
            num[temp-]++;

        ;
        dp[idx][] = dp[idx^][] = ;
        ; i<T; i++,idx^=){
            ; j<=B; j++){
                 - num[i] >= )
                    dp[idx^][j] = (dp[idx^][j-] + dp[idx][j] - dp[idx][j--num[i]] + mod)%mod;
                else
                    dp[idx^][j] = (dp[idx^][j-] + dp[idx][j])%mod;
            }
        }

        ;
        for(int i=S; i<=B; i++)
            ans = (ans + dp[idx][i])%mod;
        printf("%d\n", ans);
    }
    ;
}

POJ 3046 Ant Counting ( 多重集组合数 && 经典DP )的更多相关文章

  1. poj 3046 Ant Counting(多重集组合数)

    Ant Counting Time Limit : 2000/1000ms (Java/Other)   Memory Limit : 131072/65536K (Java/Other) Total ...

  2. poj 3046 Ant Counting

    Ant Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 4982   Accepted: 1896 Desc ...

  3. poj3046 Ant Counting——多重集组合数

    题目:http://poj.org/problem?id=3046 就是多重集组合数(分组背包优化): 从式子角度考虑:(干脆看这篇博客) https://blog.csdn.net/viphong/ ...

  4. poj 3046 Ant Counting (DP多重背包变形)

    题目:http://poj.org/problem?id=3046 思路: dp [i] [j] :=前i种 构成个数为j的方法数. #include <cstdio> #include ...

  5. POJ 3046 Ant Counting DP

    大致题意:给你a个数字,这些数字范围是1到t,每种数字最多100个,求问你这些a个数字进行组合(不包含重复),长度为s到b的集合一共有多少个. 思路:d[i][j]——前i种数字组成长度为j的集合有多 ...

  6. poj 3046 Ant Counting——多重集合的背包

    题目:http://poj.org/problem?id=3046 多重集合的背包问题. 1.式子:考虑dp[ i ][ j ]能从dp[ i-1 ][ k ](max(0 , j - c[ i ] ...

  7. POJ 3046 Ant Counting(递推,和号优化)

    计数类的问题,要求不重复,把每种物品单独考虑. 将和号递推可以把转移优化O(1). f[i = 第i种物品][j = 总数量为j] = 方案数 f[i][j] = sigma{f[i-1][j-k], ...

  8. POJ 1160:Post Office 邮局经典DP

    Post Office Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 17168   Accepted: 9270 Desc ...

  9. 多重集组合数 简单dp

    #include <cstdio> #include <iostream> using namespace std; +; +; +; ; int n,m,M; int a[m ...

随机推荐

  1. House_of_Force-ctf-bcloud

    2016 bctf bcloud 下载: https://pan.baidu.com/s/1e-fvhaOJKzBQMxlrweLznw 提取码:ded5 放入ida中首先定位到 main()-> ...

  2. 国家授时中心的NTP服务器地址 210.72.145.44

    国家授时中心的NTP服务器地址 210.72.145.44

  3. Node.js实战1:创建一个新的Node项目。

    你也许在猜专业的Node开发如何创建一个新项目. 有Npm在 ,这会非常简单. 虽然你可以创建一个JS文件,并执行:node file.js,但我建议你使用npm init来先创建一个node项目,这 ...

  4. 为企业服务器配置RAID0、raid1、 raid10、raid5、raid6、等常见RAID

    RAID卡操作手册先从开机启动时如何进入管理界面开始介绍: 1)当机器开启后,显示器出现阵列卡检测信息时,会提示用户是否要进入管理界面对阵列卡进行操作,此时按下Ctrl + H 即可,如下图 2)按下 ...

  5. VS2013启动 外接程序VMDebugger未能加载或导致了异常

    故障现象:打开Visual Studio 2010后弹出错误框,外接程序VMDebugger未能加载或导致了异常,是否希望移除该外接程序,错误号:80004005.系统版本:WIN10 64位专业版, ...

  6. Linux lvm 逻辑卷篇

    Linux LVM逻辑卷配置过程详解(创建.增加.减少.删除.卸载) 许多Linux使用者安装操作系统时都会遇到这样的困境:如何精确评估和分配各个硬盘分区的容量,如果当初评估不准确,一旦系统分区不够用 ...

  7. 怎样用adb抓取log?

    在Android客户端的测试过程中,有时候我们会遇到闪退等异常情况.这时我们可以通过adb抓取log,从而给开发提供更多信息. 一.下载ADB.exe     在网上搜索“adb工具包”就可以找到很多 ...

  8. Y7000 安装ubuntu16.04.6 的tips :禁用nouveau 、Wifi 问题 、nvidia 驱动安装

    由于最近要跑DeepLearning 所以在自己的Y7000上装一个Ubuntu ,自己碰了好多壁 写下来以防止自己忘掉以便后续再用! 配置:i7-8750H +GeForce GTX 1050Ti ...

  9. Codeforces - 1096G - Lucky Tickets - NTT

    https://codeforc.es/contest/1096/problem/G 把数组分成前后两半,那么前半部分的各个值的表示方案的平方的和就是答案. 这些数组好像可以dp出来. 一开始设dp[ ...

  10. 【算法入门】深度优先搜索(DFS)

    深度优先搜索(DFS) [算法入门] 1.前言深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍历的算法.它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解 ...