Ant Counting POJ - 3046 多重集组合数

来源:互联网 发布:中老年服饰淘宝模特 编辑:程序博客网 时间:2024/05/19 18:12

题目链接:Ant Counting POJ - 3046

t种物品,每种物品有a[i]个,从中取出s个,s+1…b个,取法数相加有多少种?(同种物品没有区别)

就是挑战上的多重集组合数

n种物品,每种物品a[i]个,从中取出m个,取法有多少?
定义dp[i+1][j] := 从前i种物品种取j个,有多少种取法;
要在i种物品中取出j个,可以在i-1中物品种取j个,第i种物品不取
或者从前i-1中物品中取j-k个,k<=min(a[i], j),第i种物品取k个
即:dp[i+1][j] = dp[i][j-k] (0<=k<=min(a[i], j)
但这样需要三层循环,效率O(n*m*a[i]);

挑战上通过将上式变形的出O(mn)的公式,看不懂变形过程,但如果从实际出发,这个公式很好理解
dp[i+1][j] = dp[i][j] + dp[i+1][j-1] - dp[i][j-1-a[i]];
右边有三个部分
第一个:在前i-1种物品中取j个,第i种物品一个都不取

第二三个整体:前i种物品取了j-1个,其中第i种取了0-a[i]-1个,然后再在第i种物品种取一个

第二个部分:前i种物品取了j-1个,其中第i种取了0-a[i]个(也就是包括了第i种物品全部取了的情况,所以要减去)
第三部分:前i个物品取了j-1个,其中第i种取了a[i]个(看起来是前i-1种物品取了j-1-a[i]个,但只要再取a[i]个i物品,就是了)

还有就是,但’j-1-a[i]<0`时,第i种不可能全部取完,就不需要减去全部取完的情况了

i = 0..n-1;j = 1..m;//(dp[i][0] = 1,因为无论i=多少,取0个的方案都只有一个,所以提前初始化dp[i][0] = 1;if(j-1-a[i]>=0) dp[i+1][j] = dp[i][j] + dp[i+1][j-1] - dp[i][j-1-a[i]];else dp[i+1][j] = dp[i][j] + dp[i+1][j-1];

而这题核心代码一样的,只需要将最后的dp[t][s…b]加起来就好了;
注意这个dp不能写成一位数组,因为每次更新都同时需要前面更新过的和没更新过的数据,但如果用二维数组,就需要dp[1000][100000],1e8的int可能会mle,所以用滚动数组就好啦

#include<cstdio>#include<cstring>#include<algorithm>#include<iostream>using namespace std;const int MAXT = 1000 + 10, MOD = 1000000;int a[MAXT], t, n, s, b, dp[2][MAXT*100];int main(){    while(cin >> t >> n >> s >> b)    {        memset(a, 0, sizeof(a));        memset(dp, 0, sizeof(dp));        int x;        for(int i=0; i<n; ++i)        {            scanf("%d", &x);            a[x-1]++;        }        dp[0][0] = dp[1][0] = 1;        for(int i=0; i<t; ++i)        {            for(int j=1; j<=b; ++j)            {                if(j-1-a[i] >= 0) dp[(i+1)&1][j] = (dp[i&1][j] + dp[(i+1)&1][j-1] - dp[i&1][j-1-a[i]] + MOD) % MOD;                else dp[(i+1)&1][j] = (dp[i&1][j] + dp[(i+1)&1][j-1]) % MOD;            }        }        int ans = 0;        for(int i=s; i<=b; ++i)        {            ans = (ans + dp[t&1][i]) % MOD;        }        cout << ans << endl;    }    return 0;}
0 0