poj-3046 Ant Counting【dp】【母函数】

题目链接:戳这里

题意:有A只蚂蚁,来自T个家族,每个家族有ti只蚂蚁。任取n只蚂蚁(S <= n <= B),求能组成几种集合?

这道题可以用dp或母函数求。

dp:参考博客戳这里

多重集组合数也是由多重背包问题拓展出来的一类经典问题。这里仍然给大家讲2种方法:

①朴素方法:

状态:dp[i][j]:前i种中选j个可以组成的种数

决策:第i种选k个,k<=ant[i] && j-k>=0

转移:dp[i][j]=Σdp[i-1][j-k]

复杂度为O(B*Σant[i])即O(B*A)也即O(A^2),虽说这题A最大可到1e5,但是实际数据水,能过

附ac代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 using namespace std;
 5 typedef unsigned long long ll;
 6 const int maxn = 1e3 + 10;
 7 const int inf = 0x3f3f3f3f;
 8 const int maxx = 1e5 + 10;
 9 int dp[2][maxx];
10 int cnt[maxn];
11 int num[maxn];
12 const int mod = 1e6;
13 int main()
14 {
15     int t, a, s ,b, u;
16     scanf("%d %d %d %d", &t, &a, &s, &b);
17     for(int i = 1; i <= a; ++i)
18     {
19         scanf("%d", &u);
20         ++cnt[u];
21     }
22     //处理边界问题,当只有一种蚂蚁的时候,无论多少个蚂蚁,组成的集合都是1
23     for(int i = 0; i <= cnt[1]; ++i) // 这里从0开始赋值是为了后面当j- k = 0时dp[][j-k]=1
24     dp[1][i] = 1;
25     for(int i = 2; i <= t; ++i)
26     {//这里j=0依然是处理边界,比如dp[3][1] = dp[2][0] + dp[2][1]
27         for(int j = 0; j <= b; ++j) 
28         {
29             for(int k = 0; k <= min(j, cnt[i]); ++k)
30             {
31                 dp[i & 1][j] = (dp[i & 1][j] + dp[(i & 1) ^ 1][j - k]) % mod;
32 
33             }
34        //     printf("%d %d %d\n", dp[i&1][j], i, j);
35         }
36         memset(dp[(i & 1) ^ 1], 0, sizeof(dp[(i & 1) ^ 1]));
37     }
38     int ans = 0;
39     for(int i = s; i <= b; ++i)
40     {
41         ans = (ans + dp[t & 1][i]) % mod;
42     }
43     printf("%d\n", ans);
44     return 0;
45 }
View Code

②优化递推式

状态:dp[i][j]:前i种中选j个可以组成的种数

决策:第i种不选或者至少选一个

转移

1.若不选,显然为dp[i-1][j]

2.若至少选一种,那么为dp[i][j-1]-dp[i-1][j-ant[i]-1]

我们这样来理解,dp[i][j-1] 理解为已经选了第i种一个,至于还选不选这里我们不管它,所以它可以用来代表至少选一个

但是dp[i][j-1]还有一层含义便是前i种中选j-1个可以组成的种数,所以它包含了选ant[i]个第i种,即dp[i-1][j-ant[i]-1],但

dp[i][j] 最多选ant[i]个第i种,所以最后要减去这一种。

所以 dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-ant[i]-1]

复杂度为O(T*B)

猜你喜欢

转载自www.cnblogs.com/zmin/p/9333159.html
ANT
今日推荐