zoukankan      html  css  js  c++  java
  • poj-3046 Ant Counting【dp】【母函数】

    题目链接:戳这里

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

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

    多重集组合数也是由多重背包问题拓展出来的一类经典问题,而此类问题也都可以用母函数求.

    给大家讲2种方法:

    ①朴素方法:

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

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

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

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

     其实这个所谓的朴素dp算法就是母函数的算法.

    附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
    ", 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
    ", ans);
    44     return 0;
    45 }
    View Code

    ②优化递推式

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

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

    转移: dp[i][j] = dp[i-1][j] + dp[i][j-1] - dp[i-1][j-cnt[i]-1] (j-cnt[i]-1>=0)

    优化思路:

    根据①可以知道

    dp[i][j]=∑{k=0~min(j,cnt[i])} dp[i][j-k]

    所以dp[i][j-1]=∑(k=0~min(j-1,cnt[i])} dp[i][j-1-k]

     二者之间的关系是:

    dp[i][j]=dp[i][j-1]+dp[i][j-1]-dp[i-1][j-cnt[i]-1] 即得出优化的转移方程.

    通俗来说,就是从前i种中取j个只与从前i种中取j-1个有两种情况不同,其他都是一样的.

    这样就省去了大量的重复运算.

    复杂度为O(T*B)

    附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                 dp[i & 1][j] = (dp[i & 1][j] + dp[(i & 1) ^ 1][j] + dp[i & 1][j - 1])%mod;
    30                 if(j - 1 - cnt[i] >= 0)
    31                     dp[i & 1][j] = (dp[i & 1][j] - dp[(i & 1) ^ 1][j - 1 - cnt[i]] + mod)%mod;//+mod防止负数
    32        //     printf("%d %d %d
    ", dp[i&1][j], i, j);
    33         }
    34         memset(dp[(i & 1) ^ 1], 0, sizeof(dp[(i & 1) ^ 1]));
    35     }
    36     int ans = 0;
    37     for(int i = s; i <= b; ++i)
    38     {
    39         ans = (ans + dp[t & 1][i])%mod;
    40     }
    41     printf("%d
    ", ans);
    42     return 0;
    43 }
    View Code

    参考博客:戳这里

  • 相关阅读:
    PAT 甲级 1027 Colors in Mars
    PAT 甲级 1026 Table Tennis(模拟)
    PAT 甲级 1025 PAT Ranking
    PAT 甲级 1024 Palindromic Number
    PAT 甲级 1023 Have Fun with Numbers
    PAT 甲级 1021 Deepest Root (并查集,树的遍历)
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯VIP 算法训练 无权最长链
    Java实现 蓝桥杯 算法提高 抽卡游戏
    Java实现 蓝桥杯 算法提高 抽卡游戏
  • 原文地址:https://www.cnblogs.com/zmin/p/9333159.html
Copyright © 2011-2022 走看看