POJ 3046 Ant Counting(“动态规划” 优化递推关系式)

http://poj.org/problem?id=3046

蚂蚁牙黑,蚂蚁牙红:有A只蚂蚁,来自T个家族。同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同。任取n只蚂蚁(S<=n<=B),求能组成几种集合?

这是《2.3 记录结果再利用的“动态规划” 优化递推关系式》练习题的第二题。

定义

dp[i][j] := 使用前i个家族可以配出来“元素个数为j”的集合的个数。

那么dp[0][0] = 1,不使用任何蚂蚁配出空集的个数为1。

递推关系式:

dp[i][j] = ∑0family[i]dp[i - 1][j - k]

前i-1个家族配成j-k的集合们每一个集合都加入k只家族i的蚂蚁,累加得到前i个家族配成j的集合的个数,直观的代码:

#include <iostream>
using namespace std;

#define MOD 1000
int family[1000 + 16];               // 每个家庭有多少只蚂蚁
int dp[1000 + 16][10000 + 16];     // 使用前i个家族可以配出来“元素个数为j”的集合的个数

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T, A, S, B;
    cin >> T >> A >> S >> B;
    for (int i = 0; i < A; ++i)
    {
        int index;
        cin >> index;
        ++family[index];
    }
    dp[0][0] = 1;
    int total = family[0];                   // 前i个家族一共有多少只蚂蚁
    for (int i = 1; i <= T; ++i)
    {
        total += family[i];
        for (int k = 0; k <= family[i]; ++k)
        {
            for (int j = total; j >= k; --j)
            {
                dp[i][j] = (dp[i][j]
                            + dp[i - 1][j - k] // 前i-1个家族配成j-k的集合们每一个集合都放入k只
                                                // 家族i的蚂蚁构成新集合,它们必然各不相同
                            ) % MOD;
            }
        }
    }
    int result = 0;
    for (int i = S; i <= B; ++i)
    {
        result = (result + dp[T][i]) % MOD;
    }
    cout << result << endl;
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    system("out.txt");
#endif
    return 0;
}
///////////////////////////End Sub//////////////////////////////////
由于dp数组每次都是i和i-1,所以可以滚动重复利用:

#include <iostream>
using namespace std;

#define MOD 1000000
int family[1000 + 16];               // 第i个家庭有多少只蚂蚁
int dp[2][100000 + 16];              // 使用前i个家族可以配出来“元素个数为j”的集合的个数

///////////////////////////SubMain//////////////////////////////////
int main(int argc, char *argv[])
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int T, A, S, B;
    cin >> T >> A >> S >> B;
    for (int i = 0; i < A; ++i)
    {
        int index;
        cin >> index;
        ++family[index];
    }
    dp[0][0] = 1;
    int total = 0;
    for (int i = 1; i <= T; ++i)
    {
        total += family[i];                       // 前i个家族一共有多少只蚂蚁
        int cur = i & 0x1;
        int pre = (i - 1) & 0x1;
        memset(dp[cur], 0, sizeof(dp[cur]));
        for (int k = 0; k <= family[i]; ++k)
        {
            for (int j = total; j >= k; --j)
            {
                dp[cur][j] = (dp[cur][j]
                            + dp[pre][j - k] // 前i-1个家族配成j-k的集合们每一个集合都放入k只
                                                // 家族i的蚂蚁构成新集合,它们必然各不相同
                            ) % MOD;
            }
        }
    }
    int result = 0;
    int cur = T & 0x1;
    for (int i = S; i <= B; ++i)
    {
        result = (result + dp[cur][i]) % MOD;
    }
    cout << result << endl;
#ifndef ONLINE_JUDGE
    fclose(stdin);
    fclose(stdout);
    system("out.txt");
#endif
    return 0;
}
///////////////////////////End Sub//////////////////////////////////
时间: 2024-10-20 13:33:36

POJ 3046 Ant Counting(“动态规划” 优化递推关系式)的相关文章

poj 3046 Ant Counting 多项式乘法解可重组合数

题意: 裸的求可重组合数. 分析: 多项式乘法求系数的应用啊,不用dp. 代码: //poj 3046 //sep9 #include <iostream> using namespace std; const int maxN=1024; const int maxL=100024; const int mod=1000000; int num[maxN]; int coef[maxL]; int tmp[maxL]; int l1; void mul(int l2) { for(int i

POJ 3046 Ant Counting ( 多重集组合数 &amp;&amp; 经典DP )

题意 : 有 n 种蚂蚁,第 i 种蚂蚁有ai个,一共有 A 个蚂蚁.不同类别的蚂蚁可以相互区分,但同种类别的蚂蚁不能相互区别.从这些蚂蚁中分别取出S,S+1...B个,一共有多少种取法. 分析 :  实际就是要解决 => 从 n 种物品中取出 m 个有多少种取法 ( 同种无法区分 ) 计数问题的 DP 定义必须保证不重复计数 这里定义 dp[i+1][j] => 从前 i 种物品中取出 j 个的组合数 根据定义为了从前 i 种物品中取出 j 个,可以从前 i-1 中取出 j-k 个并从 i

poj 3046 Ant Counting

题目大意: 有编号一到t的蚂蚁家族,每个家族有不同的蚂蚁数. 问构成S只蚂蚁到构成B只蚂蚁共有多少种方式 例如 While observing one group, the set of three ant families was seen as {1, 1, 2, 2, 3}, though rarely in that order. The possible sets of marching ants were: 3 sets with 1 ant: {1} {2} {3} 5 sets

poj 3046 Ant Counting DP

大致题意:给你a个数字,这些数字范围是1到t,每种数字最多100个,求问你这些a个数字进行组合(不包含重复),长度为s到b的集合一共有多少个. 思路:d[i][j]——前i种数字组成长度为j的集合有多少个. 那么,当前考虑第i种数字,我要组成长度为j的集合,只用在前i-1种数字所组成的集合中,只要添加0...cnt[i]个第i种数字之后长度能够达到j的那些集合数加起来 所以方程可以写成d[i][j] = ∑(cnt[i],0)  d[i-1][j-k]. 每种数字最多100个,数字最大为1000

Ant Counting (poj 3046 分组背包)

Language: Default Ant Counting Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 3142   Accepted: 1246 Description Bessie was poking around the ant hill one day watching the ants march to and fro while gathering food. She realized that man

[ACM] POJ 2154 Color (Polya计数优化,欧拉函数)

Color Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 7630   Accepted: 2507 Description Beads of N colors are connected together into a circular necklace of N beads (N<=1000000000). Your job is to calculate how many different kinds of th

POJ 2386 Lake Counting 搜索题解

简单的深度搜索就可以了,看见有人说什么使用并查集,那简直是大算法小用了. 因为可以深搜而不用回溯,故此效率就是O(N*M)了. 技巧就是增加一个标志P,每次搜索到池塘,即有W字母,那么就认为搜索到一个池塘了,P值为真. 搜索过的池塘不要重复搜索,故此,每次走过的池塘都改成其他字母,如'@',或者'#',随便一个都可以. 然后8个方向搜索. #include <stdio.h> #include <vector> #include <string.h> #include

POJ 1088 滑雪 记忆化优化题解

本题有人写是DP,不过和DP还是有点差别的,应该主要是记忆化 Momoization 算法. 思路就是递归,然后在递归的过程把计算的结果记录起来,以便后面使用. 很经典的搜索题目,这种方法很多题目考到的. 关键还是如何把代码写清晰工整了,O(∩_∩)O~. #include <stdio.h> const int MAX_N = 101; int R, C; int arr[MAX_N][MAX_N]; int tbl[MAX_N][MAX_N]; inline int max(int a,

bzoj2023[Usaco2005 Nov]Ant Counting 数蚂蚁*&amp;&amp;bzoj1630[Usaco2007 Demo]Ant Counting*

bzoj2023[Usaco2005 Nov]Ant Counting 数蚂蚁&&bzoj1630[Usaco2007 Demo]Ant Counting 题意: t个族群,每个族群有ni只蚂蚁,同族群蚂蚁没有区别.问从所有蚂蚁中选出s到b只蚂蚁有多少方案.t≤1000,ni≤100. 题解: dp,f[i][j]表示考虑第i个族群,剩下j只蚂蚁没选择.则f[i][j]=sum(f[i-1][j-k]),k=0..min(j,n[i]).然而O(n^3)会超时,注意到可以计算f[i-1][