poj3046

点击打开链接

/*
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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值