Contest Setting(dp)

 A group of contest writers have written n problems and want to use k of them in an upcoming contest. Each problem has a difficulty level. A contest is valid if all of its k problems have different difficulty levels. Compute how many distinct valid contests the contest writers can produce. Two contests are distinct if and only if there exists some problem present in one contest but not present in the other. Print the result modulo 998,244,353.

Input:

The first line of input contains two space-separated integers n and k (1 ≤ k ≤ n ≤ 1000). The next line contains n space-separated integers representing the difficulty levels. The difficulty levels are between 1 and 109 (inclusive).

Output:

Print the number of distinct contests possible, modulo 998,244,353.

Sample Input :

5 2

1 2 3 4 5

5 2

1 1 1 2 2 6

12 5

3 1 4 1 5 9 2 6 5 3 5 8

Output :

10

6

316

题目意思:

给你n到题目的难度水平,让你从中选取k道题来组成一个contest,要求这k道题难度没有相同的,让你求方案数。

Solution:

刚开始一直是往组合数方面去想,后来发现数据量不算大,就尝试往二维的dp方向去想(比赛结束也没想出来。。。)

当然,数据的范围很广,我们要把它缩小一下,因为我们并不需要具体的难度值,我们只需要相同难度的题目的个数。

我用val数组来存放不同难度题目的数量。

dp[i][j]代表前i个数中挑出j个的方案数,那么就可以这样状态转移:

dp[i][j] = dp[i - 1][j - 1] * val[i] + dp[i - 1][j];

也就是前i - 1个数挑出j个的方案数(这是之前就求出来的,相当于继承了下来),dp[i - 1][j - 1] * val[i] 这个是新增加的一道题目所带来的方案书的增加量。说的通俗一点就是:现在的 = 之前的 + 新增加的。应该能理解吧。。

最后还有一个dp数组初始化的问题(如果你很擅长处理这种小问题,下来的没必要看了),你可以观察边界状态所需要的前驱状态从而来初始化,就拿这个代码来说,我的内层for循环是从1开始跑的,那么我需要的是dp[i - 1][0]和dp[i - 1][1],那么你就必须保证每次进行内层循环的时候所需要的这两个状态是正确的,你可以在开始之前统一初始化,也可以在开始内层循环之前初始化,如果不知道如何初始化,还可以把样例拿过来参考。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

typedef long long ll;
const int mod = 998244353;
int a[1010];
ll val[1010];
ll dp[1010][1010];


int main()
{
//	freopen("in.txt", "r", stdin);
	int n, k;
	cin >> n >> k;
	for(int i = 1; i <= n; ++ i)
	{
		scanf("%d", &a[i]);
	}
	sort(a + 1, a + 1 + n);
	int cnt = 1;
	int top = 1;
	for(int i = 2; i <= n; ++ i)
	{
		if(a[i] == a[i - 1])
			cnt++;
		else 
		{
			val[top++] = cnt;
			cnt = 1;
		}
	}
	val[top++] = cnt;
	memset(dp, 0, sizeof(dp));
	for(int i = 0; i <= 1000; ++ i)
		dp[i][0] = 1;
	for(int i = 1; i < top; ++ i)
	{
		//dp[i][1] = val[i] + dp[i - 1][1];
		for(int j = 1; j <= k; ++ j)
		{
			dp[i][j] = (dp[i - 1][j - 1] * val[i] % mod + dp[i - 1][j]) % mod;
		}
	}
	cout << dp[top - 1][k] << endl;
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值