题目链接:点击打开链接
题意:给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;
}