/*
translation:
有t组蚂蚁,总数为a,同一组蚂蚁没有区别。求s~e只蚂蚁总共能划分多少组?
solution:
计数dp
设d[i][j]表示前i种蚂蚁拿出j只能划分成多少组。
则不难推出转移方程:d[i][j] = sum(d[i-1][j-k]);其中k = min(x[i], j), x[i]为i类蚂蚁的个数。
但是这样来递推复杂度太高,大概是O(t*a*sum(x[i]))。所以考虑对其优化。
当j<=x[i],将其右边展开得到d[i][j] = d[i-1][j] + d[i-1][j-1] + ... + d[i-1][0]。
将第一项d[i-1][j]单独抽离出来,就可以发现剩下的相当与d[i][j-1](根据一开始的转移方程)。
所以得到d[i][j] = d[i-1][j] + d[i][j-1]; 当j <= x[i]
接下来考虑j > x[i],同样将其展开,右边变成下式:
d[i-1][j] + d[i-1][j-1] + ... + d[i-1][j-x[i]]
为了像j<=x[i]时将后面凑出一个整项,在这个式子后再添加两项。变成如下:
d[i-1][j] + d[i-1][j-1] + ... + d[i-1][j-x[i]] + d[i-1][j-1-x[i]] - d[i-1][j-1-x[i]]
将第一项抽里出来,同样发现剩下的除了最后一项外,中间的所有项可以凑成d[i][j-1],所以此时转移方程如下:
d[i][j] = d[i-1][j] + d[i][j-1] - d[i-1][j-x[i]-1]
note:
1:由于空间限制,此道题目必须使用滚动数组。
2:注意题目要求,是要求答案的后面6为数字,所以必须mod1000000。
3:为了防止在递推中产生负数,必须要先加上1000000
4:这种类型的转移方程不能使用像01背包那样类型的滚动数组,必须用奇偶性质的滚动数组。
date:
2016.9.3
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000 + 5;
const int M = 1000000;
int x[maxn]; //每种家族的蚂蚁的个数
int t, a, s, e; //t个家族,总计a只蚂蚁,求s~e只总共能分成多少不同组
int d[2][100 * maxn]; //用i只蚂蚁总共能组成多少组,note1
int main()
{
//freopen("in.txt", "r", stdin);
while(~scanf("%d%d%d%d", &t, &a, &s, &e)) {
int tmp;
memset(x, 0, sizeof(x));
for(int i = 0; i < a; i++) {
scanf("%d", &tmp);
x[tmp]++;
}
memset(d, 0, sizeof(d));
d[0][0] = 1; d[1][0] = 1;
int k;
for(int i = 1; i <= t; i++) { //前i个蚂蚁家族
k = 1 & i;
for(int j = 1; j <= a; j++) {
if(j <= x[i]) d[k][j] = (d[k ^ 1][j] + d[k][j - 1] + M) % M; //note2
else d[k][j] = (d[k ^ 1][j] + d[k][j - 1] - d[k ^ 1][j - x[i] - 1] + M) % M; //note
}
}
int ans = 0;
for(int i = s; i <= e; i++)
if(d[k][i] >= 0) ans += d[k][i];
printf("%d\n", ans % M);
}
return 0;
}
poj3046
最新推荐文章于 2020-04-05 14:08:43 发布