Poj-3046 多重集合组合数

https://vjudge.net/problem/POJ-3046

有T种物品,每种物品有a[i]个,同种类物品不区分,从中取出i个的方法为f(i),求\large \sum_{i=S}^{B}f(i)

设dp[i][j]是前i种物品,拿出j个方法数

那么对每一层i,其中的每个dp[i][j]


,dp[i][j]=\large \sum_{k=0}^{min(a[i],j)}dp[i-1][j-k]
划分的再详细一点:

对于j>=a[i]的,dp[i][j]=\sum_{k=1}^{j}dp[i-1][j]

对于a[i]>=j的,dp[i][j]=\sum_{k=1}^{a[i]}dp[j-k]

这样就能 算出所有的dp[i][j],而答案就是\sum_{j=S}^{B}dp[T][j]

但是直接这样算,复杂度肯定会爆的,这个时候,就有一个本菜鸡还不熟练的思想,利用前面计算的结果

对于j>=a[i]的,dp[i][j]=dp[i][j-1]+dp[i-1][j]

对于j<a[i],dp[i][j]相比dp[i][j-1]增加了一个dp[i-1][j],减少了一个dp[i-1][j-a[i]-1],再加上就好啦

所以dp[i][j]=dp[i][j-1]+dp[i-1][j]-dp[i-1][j-a[i]-1],

终于AC的代码,一定要吸取到这种思想,这次还在Onenote上给自己做题的顺序,流程做了规定

希望能遵守,希望能减少Bug

代码:

//Problem:
//Date:
//Skill:
//Bug:
/definations
#define CLR(a) memset((a),0,sizeof(a))
#define RE(i,n)  for(int i=0;i<n;i++)
#define RE2(i,n) for(int i=1;i<=n;i++)
#define IN(n) scanf("%d",&n)
#define LLIN(n) scanf("%lld",&n)
#define INC(c) do{scanf("%c",&c);}while(isspace(c))
#define PI(a) printf("%d",a)
#define LLPI(a) printf("%lld",a)
#define PN	puts("");
#include<string.h>
#include<math.h>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
const long long llinf = 0x3f3f3f3f3f3f3f3f;
const int inf = 0x3f3f3f3f;
///

options
const int maxn = int();
typedef long long ll;
#define CPP_IO
//#define LOCAL
#ifdef CPP_IO
#include<iostream>
#include<iomanip>
#include<string>
#else
#include<stdio.h>
#endif
///

Added Functions/
int a[1010];
int dp[1010][100010];

///
int main()
{
#ifdef LOCAL
	freopen("C:\\Users\\VULCAN\\Desktop\\data.in", "r", stdin);
#endif //LOCAL
#ifdef CPP_IO
	std::ios::sync_with_stdio(false);
	cin.tie(0); cout.tie(0);
#endif // CPP_IO

	//How to think:
	//先认真读题,把题目逻辑化,读清全部条件,再做,不然,哼哼
	//写题的时候遇到的一些”莫名“的错误大部分是自己的代码写错了一个变量
	//1.分治 2.容斥 3.化多元为单元 
	//4.dp 分阶段,后阶段受前阶段的影响
	//5.凡是有很大的指数的,都可以先用log算一下
	//6.预处理,作用非常大,很快
	//
	//1.Do you use cout/cin? Do not use!
	//2.what's the maximum Int data? Is it beyond I32d?
	//3.Ever Mod after neccesary calculation?
	//4.Ever Cleaned the array before using?
	///code
	int T(1), times(0);
	//IN(T);
	while (++times, T--)
	{
		int temp, mod = 1000000;
		int T, A, S, B; cin >> T >> A >> S >> B;
		memset(a, 0, sizeof(a));
		RE2(i, A)
			cin >> temp,a[temp]++;
		RE(i, T + 1)dp[i][0] = 1;
		RE2(i, T)
		{
			RE2(j, B)
			{
				if (j > a[i])
				{
					dp[i][j] = dp[i - 1][j]
						+ dp[i][j - 1]/*加一个*/
						- dp[i - 1][j-1- a[i]];
					dp[i][j] = (dp[i][j] + mod) % mod;
				}
				else
					dp[i][j] = (dp[i][j - 1] + dp[i - 1][j]) % mod;
			}
		}
		ll ans(0);
		for (int i = S; i <= B; ++i)ans += dp[T][i],ans%=mod;
		cout << ans << endl;

	}
	//
	//dividing line
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值