POJ 3046---Ant Counting
http://poj.org/problem?id=3046
多重组合数问题原型:
有n种物品,第i种物品有a[i]个。不同种类可以取分但相同种类物品无法区分,从这些物品中取出m个的话,有多少种取法。
题意理解:
有T种蚂蚁,问这些蚂蚁分别取出 S.S+1...B 个的话,有多少种取法,就是原型中有若干个不同的m。
dp[i][j]:从前i种物品中取出j个的组合总种类数。
递推关系:从前i-1种取出j-k个,第i种取出k个。
dp[i][j]=∑dp[i-1][j-k](k从0到min(j,a[i])
一般这种连加符号或者max(完全背包不就是?)都根据递推定义,分离一项进行化简。
①:当j<=a[i]:
dp[i][j]=∑dp[i-1][j-k](k从0到j:j,j-1...1,0)
=dp[i-1][j]+∑dp[i-1][j-k](k从1到j:j-1..0)
=dp[i-1][j]+∑dp[i-1][j-1-(k-1)]
=dp[i-1][j]+dp[i][j-1]
②:当a[i]<j:
dp[i][j]=∑dp[i-1][j-k](k从0到a[i]:j,j-1...j-a[i])
=dp[i-1][j]+∑dp[i-1][j-k](k从1到a[i]:j-1...j-a[i])
=dp[i-1][j]+∑dp[i-1][j-1-(k-1)] (凑dp[i][j-1],先+再-)
=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1-a[i]]
初始化:dp[i][0]=1(一个都不取的方法总是只有1种)
注意:不能用一维数组,原因:当前位置的状态取决于上,左上,和左。左上要逆序,左要顺序,矛盾!所以只能滚动数组了。
AC代码
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define M (int)1e6
int dp[2][100*1000+5];
int ant[1005];
int main()
{
int T, A, S, B;
cin >> T >> A >> S >> B;
for (int i = 1; i <= A; i++)
{ int x; scanf("%d", &x);
ant[x]++;
}
dp[0][0] = dp[1][0] = 1;
for (int i = 1; i <= T; i++)
for (int j = 1; j <= B; j++)
{
if (j >ant[i])
dp[i % 2][j] = (dp[i % 2][j - 1] + dp[(i-1) % 2][j] - dp[(i-1) % 2][j - 1 - ant[i]] + M) % M;
else dp[i % 2][j] = (dp[i % 2][j - 1] + dp[(i - 1) % 2][j]) % M;
}
int ans=0;
for (int i = S; i <= B; i++)
ans = (ans + dp[T % 2][i]) % M;
cout << ans<<endl;
system("pause");
}