【NOIP2017提高A组集训10.30】Group

Description:

这里写图片描述
1<=n<=200,1<=k<=1000

题解:

首先肯定得排个序,再考虑如何dp。
一个组的贡献可以是最大值减最小值,也可以是相邻的两个数的差的和。
这样就可以dp了,对于一个组,如果它没有结尾,那么每往后移以下,贡献就会增加。
可以设 fi,j,k 表示当前做到第i个数,有j个组还没有结尾,贡献和已经是k的方案数。
对于新来的一个数,有4种情况:
1.新开一组但不做结尾。
2.新开一组且做结尾。
3.接到已有的一组后但是不做结尾。
4.接到已有的一组后并做结尾。

根据这4种情况可得方程。

Code:

#include
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int mo = 1e9 + 7;

const int N = 201, M = 1001;

int n, k, o, a[N], f[2][N][M];

int main() {
    freopen("group.in", "r", stdin);
    freopen("group.out", "w", stdout);
    scanf("%d %d", &n, &k);
    fo(i, 1, n) scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
    f[o][1][0] = f[o][0][0] = 1;
    fo(i, 2, n) {
        o = !o;
        memset(f[o], 0, sizeof f[o]);
        fo(j, 0, i - 1) {
            fo(p, 0, k) {
                int v = p + j * (a[i] - a[i - 1]);
                if(v > k) break;
                f[o][j + 1][v] += f[!o][j][p]; f[o][j + 1][v] %= mo;
                f[o][j][v] += f[!o][j][p]; f[o][j][v] %= mo;
                if(j) f[o][j][v] += (ll)f[!o][j][p] * j % mo, f[o][j][v] %= mo;
                if(j) f[o][j - 1][v] += (ll)f[!o][j][p] * j % mo, f[o][j - 1][v] %= mo;
            }
        }
    }
    int ans = 0;
    fo(p, 0, k) ans = (ans + f[o][0][p]) % mo;
    printf("%d", ans);
}

你可能感兴趣的:(动态规划)