题意:
有n个人完成一项团队项目,第i个人可花费\(a_i\)时间独立完成自己的一份工作,把n个人划分成多组,定义每组的imbalance为其中的最大值减最小值,求有多少种分组方案使各组的imbalance之和最多为k。
思路:
首先对\(a_i\)排序,然后利用动态规划来做。设\(dp[i][j][t]\)为考虑了i个人,有j个组是开放的(只有\(a_i\)的最小值,没有最大值,组内还可以继续加人),imbalance之和为t。对第i个人有四种转移:
1、第i个人自己单独一个非开放的组;
2、第i个人进入j个开放的组中的某一个;
3、第i个人作为某个开放的组的最大值,从而关闭这个组。
4、第i个人新开一个开放的组;
其中第i个人对imbalance之和的增加量为\(j*(a[i]-a[i-1])\) 这个地方想了好久才明白,加第i个人时,只会对当前开放的j个组有影响,因为其他的最大值已经确定,对于每个开放的组,我们可以将它们的最值都增加\(a[i]-a[i-1]\),这样可以得到\(a[i]\),并且相当于虚拟的把a[i]放进各组中,最后可以确定各组的最大值。
可以用滚动数组减少空间占用。
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
#define FIO ios_base::sync_with_stdio(false); cin.tie(nullptr);
const int MOD = 1000000007;
long long dp[2][201][1001];
int main()
{
FIO
int n, k;
cin >> n >> k;
int a[201] = {0};
for (int i = 1; i <= n; ++i)
cin >> a[i];
sort(a + 1, a + n + 1);
int u = 0;
dp[u][0][0] = 1;
for (int i = 1; i <= n; ++i)
{
u ^= 1;
memset(dp[u], 0, sizeof(dp[u]));
for (int j = 0; j <= n; ++j)
{
int add = a[i] - a[i - 1];
for (int t = 0; t <= k && t + j * add <= k; ++t)
if (dp[u ^ 1][j][t])
{
dp[u][j][t + j * add] = (dp[u][j][t + j * add] + dp[u ^ 1][j][t]) % MOD;
dp[u][j][t + j * add] = (dp[u][j][t + j * add] + dp[u ^ 1][j][t] * j % MOD) % MOD;
if (j > 0)
dp[u][j - 1][t + j * add] = (dp[u][j - 1][t + j * add] + dp[u ^ 1][j][t] * j % MOD) % MOD;
dp[u][j + 1][t + j * add] = (dp[u][j + 1][t + j * add] + dp[u ^ 1][j][t]) % MOD;
}
}
}
long long ans = 0;
for (int i = 0; i <= k; ++i)
ans = (ans + dp[u][0][i]) % MOD;
cout << ans << '\n';
return 0;
}