『计数DP优化』CF626F Group Projects

该博客介绍了如何利用计数动态规划(DP)解决一类关于学生分组问题,其中目标是使所有组的不平衡度总和不超过k。博主详细分析了问题解决方案,包括四种情况的贡献,并提出了通过计算差分简化第三维处理的优化技巧。最后给出了简化后的递推公式和最终答案的计算方法。
摘要由CSDN通过智能技术生成

P r o b l e m \mathrm{Problem} Problem

There are n n n students in a class working on group projects. The students will divide into groups (some students may be in groups alone), work on their independent pieces, and then discuss the results together. It takes the i i i -th student a i a_{i} ai minutes to finish his/her independent piece.

If students work at different paces, it can be frustrating for the faster students and stressful for the slower ones. In particular, the imbalance of a group is defined as the maximum a i a_{i} ai in the group minus the minimum a i a_{i} ai in the group. Note that a group containing a single student has an imbalance of 0 0 0 . How many ways are there for the students to divide into groups so that the total imbalance of all groups is at most k k k ?

Two divisions are considered distinct if there exists a pair of students who work in the same group in one division but different groups in the other.


S o l u t i o n \mathrm{Solution} Solution

这道题,我们首先想到排序,对于一个数字我们可以分成 4 4 4 类:贡献为 − 1 -1 1 的最小值,贡献为 1 1 1 的最大值,贡献为 0 0 0 的中间值或单独一组。

我们我们设 f ( i , j , k ) f(i,j,k) f(i,j,k) 表示前 i i i 个数,有 j j j 组数需要求出最大值,和为 k k k 的方案。

  • 当前数为最小值时,贡献为 f ( i − 1 , j − 1 , k + a i ) f(i-1,j-1,k+a_i) f(i1,j1,k+ai)
  • 当前数为最大值时,贡献为 f ( i − 1 , j + 1 , k − a i ) × ( j + 1 ) f(i-1,j+1,k-a_i)\times (j+1) f(i1,j+1,kai)×(j+1),表示 j + 1 j+1 j+1个组任意挑选一个组来进行结尾。
  • 当前数为中间值时,贡献为 f ( i − 1 , j , k ) × j f(i-1,j,k)\times j f(i1,j,k)×j
  • 当前数单独为一组时,贡献为 f ( i − 1 , j , k ) f(i-1,j,k) f(i1,j,k)

我们发现第三维难以处理,考虑优化。

我们发现对于 [ l , r ] [l,r] [l,r]的贡献,可以简化为 ∑ i = l + 1 r a i − a i − 1 \sum_{i=l+1}^{r} a_i-a_{i-1} i=l+1raiai1,那么我们只要对差分进行求和即可。对于每一组没有结尾的或者刚好结尾的,我们加上这个差分值。

然后就简化成了: f ( i , j , k ) = f ( i − 1 , j − 1 , k − ( j − 1 ) △ ) + ( j + 1 ) × f ( i − 1 , j + 1 , k − ( j + 1 ) △ ) + j × f ( i − 1 , j , k − j △ ) + f ( i − 1 , j , k − j △ ) f(i,j,k)=f(i-1,j-1,k-(j-1)△)\\+(j+1)\times f(i-1,j+1,k-(j+1)△)+\\j\times f(i-1,j,k-j△)+f(i-1,j,k-j△) f(i,j,k)=f(i1,j1,k(j1))+(j+1)×f(i1,j+1,k(j+1))+j×f(i1,j,kj)+f(i1,j,kj)
答案是 ∑ f ( n , 0 , i ) ( i ≤ m ) \sum f(n,0,i)(i\le m) f(n,0,i)(im)


C o d e \mathrm{Code} Code

#include <bits/stdc++.h>
#define f(a, b, c) f[a][b][c]
#define update(a, b) a = (a + b) % P

using namespace std;
const int P = 1e9 + 7;
const int N = 210;
const int K = 1010;

int n, m;
int a[N], f[N][N][K];

int read(void)
{
	int s = 0, w = 0; char c = getchar();
	while (!isdigit(c)) w |= c == '-', c = getchar();
	while (isdigit(c)) s = s*10+c-48, c = getchar();
	return w ? -s : s;
}

signed main(void)
{
	n = read(), m = read();
	for (int i=1;i<=n;++i) a[i] = read();
	sort(a+1, a+n+1);
	f[0][0][0] = 1;
	for (int i=1;i<=n;++i) {
		for (int j=0;j<=i;++j) {
			int val = a[i] - a[i-1];
			for (int k=0;k<=m;++k) {
				if (k - j * val >= 0) update(f(i, j, k), f(i-1, j, k - j * val));
				if (k - j * val >= 0) update(f(i, j, k), 1LL * f(i-1, j, k - j * val) * j % P);
				if (j > 0 and k - (j-1) * val >= 0) update(f(i, j, k), f(i-1, j-1, k-(j-1)*val));
				if (j < n and k - (j+1) * val >= 0) update(f(i, j, k), 1LL * f(i-1, j+1, k-(j+1)*val) * (j+1) % P); 
			}
		}
	}
	int res = 0; 
	for (int i=0;i<=m;++i) update(res, f(n, 0, i));
	cout << res << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值