8VC Venture Cup 2016 - Elimination Round F. Group Projects(DP)

题目链接:点击打开链接

题意:给n个人, 让我们分成若干组, 每组的值是最大值减去最小值, 求所有组的和。

思路:显然, 需要用DP的思想, 但是该题DP设计的非常巧妙, 而且状态转移的情况容易考虑不全。

我们用d[i][j][v]表示考虑了前i个数了, 有j个组是开放的(所谓开放指的是只有最小值, 还没有最大值, 还可以进人), 当前值之和为v 的方案数。

我们先排序, 这样, 对于开放的组, 每次的累加量就都是 j*(a[i] - a[i-1])。

那么转移的情况要考虑这么几个:

1. 第i个数单组一组

2.第i个数新开一组, 作为新组的最小值

3.第i个数关闭一组, 作为这个组的最大值。

4.第i个数进入j个组中的某一组。

需要用滚动数组, 否则会爆内存。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 200 + 10;
int T,n,k,a[maxn], vis[maxn][maxn][1000 + 10], kase=1;
ll d[2][maxn][1000 + 10];
int main() {
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1, a+n+1);
    a[0] = a[1];
    int u = 0;
    d[u][0][0] = 1;
    for(int i=1;i<=n;i++) {
        u ^= 1;
        memset(d[u], 0, sizeof(d[u]));
        for(int j=0;j<=n;j++) {
            int add = (a[i] - a[i-1]);
            for(int v=0;v<=k;v++) {
                if(!d[u^1][j][v]) continue;
                if(v + j*add > k) break; //剪枝, 别忘了, 取模是很费时间的
                d[u][j][v+j*add] = (d[u][j][v+j*add] + d[u^1][j][v])%mod;//自己一组
                d[u][j][v+j*add] = (d[u][j][v+j*add] + d[u^1][j][v]*j%mod)%mod;//随便扔到一组
                if(j > 0) {
                    d[u][j-1][v+j*add] = (d[u][j-1][v+j*add] + d[u^1][j][v]*j%mod)%mod;//关闭一组,作为终点
                }
                d[u][j+1][v+j*add] = (d[u][j+1][v+j*add] + d[u^1][j][v])%mod;//开启一组,作为起点
            }
        }
    }
    ll ans = 0;
    for(int i=0;i<=k;i++) {
        ans = (ans + d[u][0][i])%mod;
    }
    printf("%I64d\n",ans);
    return 0;
}



你可能感兴趣的:(dp,codeforces)