题目链接:http://codeforces.com/contest/626/problem/F
题意:
有n个人,每个人有能力值ai,你需要去将这n个人分组
每一组的分值是这一个组的最大值减去最小值,你需要使得所有组的分值和小于等于k
问你方案数一共有多少种
解法:
dp
我们先排序,这样好DP
dp[i][j][k]表示考虑了i个人,现在open的组有j个(只有最小值,没有最大值的组),分值和为k的方案数
我们考虑第i个人加进去的时候,他对分值的贡献,应该是j*(a[i]-a[i-1]),不管这个数加到哪个组里面,贡献都是这个。
为什么?因为open的都在等待最大值的到来咯,考虑差分。
然后转移的时候,可以新开一个open,可以转移到open里面去,可以关闭一个open
空间开不下,滚一下就好了。
复杂度:O(n*n*k)
//CF 626F
#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
template <class T1, class T2> inline void add(T1 &x, T2 y) {x = (x + y); if(x >= mod) x -= mod;}
int dp[2][205][1005];///考虑了i个人,现在open的组有j个(只有最小值,没有最大值的组),分值和为k的方案数
int a[1005];
int n, k;
int main()
{
scanf("%d%d", &n, &k);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
sort(a+1, a+n+1);
dp[0][0][0] = 1;
for(int i = 1; i <= n; i++){
memset(dp[1], 0, sizeof(dp[1]));
for(int j = 0; j <= n; j++){
for(int p = 0; p <= k; p++){
if(p + j*(a[i]-a[i-1]) <= k){
add(dp[1][j][p+j*(a[i]-a[i-1])], 1LL*dp[0][j][p]%mod);
if(j >= 1){
add(dp[1][j-1][p+j*(a[i]-a[i-1])], 1LL*dp[0][j][p]*j%mod);
add(dp[1][j][p+j*(a[i]-a[i-1])], 1LL*dp[0][j][p]*j%mod);
}
add(dp[1][j+1][p+j*(a[i]-a[i-1])], dp[0][j][p]%mod);
}
}
}
memcpy(dp[0], dp[1], sizeof(dp[0]));
}
int ans = 0;
for(int i = 0; i <= k; i++){
add(ans, dp[0][0][i]);
}
printf("%d\n", ans);
return 0;
}