http://poj.org/problem?id=3046
https://www.cnblogs.com/demian/p/7367497.html
dp[i][j]=∑k=0a[i]dp[i−1][j−k]
for (int i = 1; i <= t; ++i) {
for (int j = 1; j <= a; ++j) {
if (j - 1 - num[i] >= 0) {
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1 - num[i]] + MOD) % MOD;
}
else dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % MOD;
}
}
//for (int i = 1; i <= t; ++i, cout << '\n') for (int k = 0; k <= a; ++k) cout << dp[i][k] << ' ';
int ans = 0;
for (int i = s; i <= b; ++i) {
ans = (ans + dp[t][i]) % MOD;
}
dp[i][j]=∑k=0α[i]dp[i−1][j−k]=dp[i−1][j]+∑k=1α[i]dp[i−1][j−k]=dp[i−1][j]+∑k=0α[i]−1dp[i−1][j−k−1]=dp[i−1][j]+∑k=0α[i]dp[i−1][j−1−k]−dp[i−1][j−1−α[i]]=dp[i−1][j]+dp[i][j−1]+dp[i−1][j−1−α[i]]
public static int solve(int T, int A, int S, int B, int[] family){
int[][] dp = new int[2][A+1];
dp[0][0] = dp[1][0] = 1;
int total = A;
for (int i = 1; i <= T; ++i){
for (int j = 1; j <= total; ++j){
if (j - 1 - family[i] >= 0){
dp[i%2][j] = (dp[i%2][j-1] - dp[(i-1)%2][j-1-family[i]] + dp[(i-1)%2][j] + MOD) % MOD;
}else{
dp[i%2][j] = (dp[i%2][j-1] + dp[(i-1)%2][j] ) % MOD;
}
}
}
int ans = 0;
for (int i = S; i <= B; ++i){
ans = (ans + dp[T%2][i]) % MOD;
}
return ans;
}
http://www.hankcs.com/program/cpp/poj-3046-ant-counting-problem-solution-challenge-programming-contest-2nd-edition.html
蚂蚁牙黑,蚂蚁牙红:有A只蚂蚁,来自T个家族。同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同。任取n只蚂蚁(S<=n<=B),求能组成几种集合?
由于dp数组每次都是i和i-1,所以可以滚动重复利用:
Read full article from 3046 -- Ant Counting
Description
Bessie was poking around the ant hill one day watching the ants march to and fro while gathering food. She realized that many of the ants were siblings, indistinguishable from one another. She also realized the sometimes only one ant would go for food, sometimes a few, and sometimes all of them. This made for a large number of different sets of ants!
Being a bit mathematical, Bessie started wondering. Bessie noted that the hive has T (1 <= T <= 1,000) families of ants which she labeled 1..T (A ants altogether). Each family had some number Ni (1 <= Ni <= 100) of ants.
How many groups of sizes S, S+1, ..., B (1 <= S <= B <= A) can be formed?
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 with 2 ants: {1,1} {1,2} {1,3} {2,2} {2,3}
5 sets with 3 ants: {1,1,2} {1,1,3} {1,2,2} {1,2,3} {2,2,3}
3 sets with 4 ants: {1,2,2,3} {1,1,2,2} {1,1,2,3}
1 set with 5 ants: {1,1,2,2,3}
Your job is to count the number of possible sets of ants given the data above.
Being a bit mathematical, Bessie started wondering. Bessie noted that the hive has T (1 <= T <= 1,000) families of ants which she labeled 1..T (A ants altogether). Each family had some number Ni (1 <= Ni <= 100) of ants.
How many groups of sizes S, S+1, ..., B (1 <= S <= B <= A) can be formed?
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 with 2 ants: {1,1} {1,2} {1,3} {2,2} {2,3}
5 sets with 3 ants: {1,1,2} {1,1,3} {1,2,2} {1,2,3} {2,2,3}
3 sets with 4 ants: {1,2,2,3} {1,1,2,2} {1,1,2,3}
1 set with 5 ants: {1,1,2,2,3}
Your job is to count the number of possible sets of ants given the data above.
Input
* Line 1: 4 space-separated integers: T, A, S, and B
* Lines 2..A+1: Each line contains a single integer that is an ant type present in the hive
* Lines 2..A+1: Each line contains a single integer that is an ant type present in the hive
Output
* Line 1: The number of sets of size S..B (inclusive) that can be created. A set like {1,2} is the same as the set {2,1} and should not be double-counted. Print only the LAST SIX DIGITS of this number, with no leading zeroes or spaces.
Sample Input
3 5 2 3 1 2 2 1 3
Sample Output
10X. O(KN^2)
https://www.cnblogs.com/demian/p/7367497.html
题意:给出T种数字。每种各有N[i]个。然后用这些数字构成一些序列, 问x长度到y长度的序列有多少种
思路:
dp[i][j] 表示前i种数字构成长度为j的序列有多少种。
dp[i][j] = sigma(dp[i - 1][j - k]) k的范围是0~N[i]
//前i-1个家族配成j-k的集合,每一个集合都加入家族i的k只的蚂蚁,累加得到前i个家族配成j的集合的个数:
- dp[0][0]=1;// 空集的个数为一
- int totel=family[0];
- for(int i=1;i<=T;i++)
- {
- totel+=family[i]; // 前i个家族一共有多少只蚂蚁
- for(int k=0;k<=family[i];k++)
- {
- for(int j=totel;j>=k;j--)
- {
- dp[i][j]=(dp[i][j]+dp[i-1][j-k])%MOD; // 前i-1个家族配成j-k的集合们每一个集合都放入k只
- }
- }
- }
- int result=0;
- for (int i = S; i <= B; ++i)
- {
- result = (result + dp[T][i]) % MOD;
- }
蚂蚁牙黑,蚂蚁牙红:有A只蚂蚁,来自T个家族。同一个家族的蚂蚁长得一样,但是不同家族的蚂蚁牙齿颜色不同。任取n只蚂蚁(S<=n<=B),求能组成几种集合?
一道排列组合题,问题可以归简为:从元素为n个的集合中,取出m个元素,总共有多少种取法?
dp[i][j] 表示前i个家族中,取出j个元素的组合总数
初始化:
dp[0][0] = 1, 表示无蚂蚁,取出0个元素。(空集)
递推式:
按书中的定义得,从前i 个家族中取出j 个,可以从前i−1 个家族中取出j−k 个,再从第i 个家族中取出k 个添加进来。
两点:
dp[i−1][j−k] 的值代表了取j−k 个元素的组合数,每个组合都是唯一代表一个集合。- 当有
k 个相同的元素加入到新的集合中,就有了dp[i][j]=1×dp[i−1][j−k] ,因为第i 个家庭的k 个元素组合始终为1,该家族的每个元素无差别。
X. https://blog.csdn.net/CatGlory/article/details/50519320
https://shanzi.gitbooks.io/algorithm-notes/content/problem_solutions/ant_counting.html
for (int i = 0; i <= t; ++i) dp[i][0] = 1;https://shanzi.gitbooks.io/algorithm-notes/content/problem_solutions/ant_counting.html
for (int i = 1; i <= t; ++i) {
for (int j = 1; j <= a; ++j) {
if (j - 1 - num[i] >= 0) {
dp[i][j] = (dp[i][j - 1] + dp[i - 1][j] - dp[i - 1][j - 1 - num[i]] + MOD) % MOD;
}
else dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % MOD;
}
}
//for (int i = 1; i <= t; ++i, cout << '\n') for (int k = 0; k <= a; ++k) cout << dp[i][k] << ' ';
int ans = 0;
for (int i = s; i <= b; ++i) {
ans = (ans + dp[t][i]) % MOD;
}
上述时间和空间都不尽人如意,递推式优化:
http://www.hankcs.com/program/cpp/poj-3046-ant-counting-problem-solution-challenge-programming-contest-2nd-edition.html
蚂蚁牙黑,蚂蚁牙红:有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的集合的个数,直观的代码:
#define MOD 1000
int
family[1000 + 16];
// 每个家庭有多少只蚂蚁
int
dp[1000 + 16][10000 + 16];
// 使用前i个家族可以配出来“元素个数为j”的集合的个数
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;
}
#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;
}