poj3064 Ant Counting(动态规划)
题目链接:http://poj.org/problem?id=3046
题目大意:有T(1<=T<=1000)种蚂蚁,共有A只,每种最多有Ni(1<=Ni<=100)。不同种类的蚂蚁可以相互区分但相同种类无法区分。从这些蚂蚁中挑取S种,S+1种…B种,共有多少种取法,求出方案数膜1000000的余数。
动态规划策略:设dp[i][j]为前i种蚂蚁取出j只出来的方案数。
则状态转移方程为:dp[i][j] = dp[i-1][j] + dp[i][j-1]
这种取法有点类似于01完全背包,区别只是这个背包有上限,而完全背包无上限。
所以当j>ant[i](第i种蚂蚁的数目)时,dp[i][j]的值还要减去dp[i-1][j-a[i]-1]
意思是要除去从i-1种蚂蚁中取j-a[i]-1只,在从第i种蚂蚁种取a[i]+1只的情况。
动态规划方程:所以最终的动态规划方程如下:
if( j > a[i] ):
dp[i][j] = (dp[i-1][j] + dp[i][j-1] - dp[i-1][j-a[i]-1] + M) % M;
else:
dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % M;
注意这里有个+M然后在对M求模,是因为前面做了减法,可能会出现负值,因此先加上一个M然后在求模就能保证结果为正了。
动态规划的初值:对于dp的初值只需要考虑对于每个i当j=0的时候,很容易发现dp[i][0]=1。因为只有一种取法,那就是不取。
下面是已ac的代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define M 1000000
int T, A, S, B;
int a[1010]; // ant family[i] 有a[i]个ant
int dp[1010][10010];// dp[i][j] 前i个取j个的取法总数
int main()
{
int x;
scanf( "%d%d%d%d", &T, &A, &S, &B );
for ( int i = 0 ; i < A ; ++ i ) {
scanf( "%d", &x );
a[x]++;
}
// 取n个的取法总数
int res = 0;
for ( int i = 0 ; i <= T ; ++ i ) dp[i][0] = 1;// 取0个总有一种方法
for ( int i = 1 ; i <= T ; ++ i ) {
for ( int j = 1 ; j <= B ; ++ j ) {
if ( j > a[i] ) {
dp[i][j] = (dp[i-1][j] + dp[i][j-1] - dp[i-1][j-a[i]-1] + M)%M;
} else {
dp[i][j] = (dp[i-1][j] + dp[i][j-1]) % M;
}
}
}
for ( int i = S ; i <= B ; ++ i ) {
res = (res+dp[T][i]) % M;
}
printf( "%d\n", res );
return 0;
}