题目链接: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;
}