8VC Venture Cup 2016 - Elimination Round F - Group Projects dp好题

F - Group Projects

题目大意:给你n个物品, 每个物品有个权值ai, 把它们分成若干组, 总消耗为每组里的最大值减最小值之和。

问你一共有多少种分组方法。

 

思路:感觉刚看到的时候的想法是先排好序, dp[ i ][ j ][ k ]表示已经划分了 i 个, 并且分成了 j 组, 目前为止的总消耗为k的方案总数。

但是这个状态没有办法转移, 题解提供了一种将贡献分段的方法, 比如说原数组为1 2 3 6 7 8 10, 其中一组里面有 1 3 7 8 那么我们不是

直接计算出(8 - 1) 而是在dp的过程中计算 (2 - 1) + (3 - 2) + (6 - 3) + (7 - 6) + (8  - 7),每个数字累加上去的。

所以我们可能得到dp状态dp[ i ][ j ][ k ]表示已经用了前 i 个物品, 有 j 个组还能继续往里加数字, 总的消耗为k的方案数。

然后就能很容易写出dp方程啦。

#include<bits/stdc++.h>
#define LL long long
#define fi first
#define se second
#define mk make_pair
#define PII pair<int, int>
#define PLI pair<LL, int>
#define ull unsigned long long
using namespace std;

const int N = 200 + 7;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
const double eps = 1e-8;

int n, m, cur, a[N], dp[2][207][1007];

void add(int &a, int b) {
    a += b; if(a >= mod) a -= mod;
}

int main() {
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
    sort(a + 1, a + 1 + n);
    dp[cur][0][0] = 1;
    for(int i = 1; i <= n; i++) {
        cur = cur ^ 1;
        memset(dp[cur], 0, sizeof(dp[cur]));
        for(int j = 0; j <= i; j++) {
            int cost = j*(a[i]-a[i-1]);
            for(int k = 0; k + cost <= m; k++) {
                add(dp[cur][j+1][k+cost], dp[cur^1][j][k]);
                add(dp[cur][j][k+cost], dp[cur^1][j][k]);
                add(dp[cur][j][k+cost], 1ll*j*dp[cur^1][j][k]%mod);
                if(j) add(dp[cur][j-1][k+cost], 1ll*j*dp[cur^1][j][k]%mod);
            }
        }
    }
    int ans = 0;
    for(int i = 0; i <= m; i++)
        add(ans, dp[cur][0][i]);
    printf("%d\n", ans);
    return 0;
}

/*
*/

 

posted @ 2018-11-03 20:40  NotNight  阅读(142)  评论(0编辑  收藏  举报