【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<cstdio>
#include<algorithm>
#include<cstring>
#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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值