CF626F:Group Projects
There are n students in a class working on group projects. The students will divide into groups (some students may be in groups alone), work on their independent pieces, and then discuss the results together. It takes the i-th student ai minutes to finish his/her independent piece.
If students work at different paces, it can be frustrating for the faster students and stressful for the slower ones. In particular, the imbalanceof a group is defined as the maximum ai in the group minus the minimum ai in the group. Note that a group containing a single student has an imbalance of 0. How many ways are there for the students to divide into groups so that the total imbalance of all groups is at most k?
Two divisions are considered distinct if there exists a pair of students who work in the same group in one division but different groups in the other.
The first line contains two space-separated integers n and k (1 ≤ n ≤ 200, 0 ≤ k ≤ 1000) — the number of students and the maximum total imbalance allowed, respectively.
The second line contains n space-separated integers ai (1 ≤ ai ≤ 500) — the time it takes the i-th student to complete his/her independent piece of work.
Print a single integer, the number of ways the students can form groups. As the answer may be large, print its value modulo 109 + 7.
3 2
2 4 5
3
4 3
7 8 9 10
13
4 0
5 10 20 21
1
In the first sample, we have three options:
- The first and second students form a group, and the third student forms a group. Total imbalance is 2 + 0 = 2.
- The first student forms a group, and the second and third students form a group. Total imbalance is 0 + 1 = 1.
- All three students form their own groups. Total imbalance is 0.
In the third sample, the total imbalance must be 0, so each student must work individually.
题解:
非常妙的一道DP题
显然,每个集合中的关键在于其最大值与最小值
定义一个开放的集合为一个只有最小值而没有最大值(即暂时未加入其最大值)的集合,将序列排序后,从小到大依次加入每个a[i],那么一个集合的代价,可视为闭合前每个(a[i+1]-a[i])的累加(因为对于每个开放的集合,即使你不加入当前的a[i],后续也必定会加入另一些更大的元素来闭合它,所以可以把最终的代价拆分为多段,每段加一些,将本应最后计算的代加在每次转移过程中动态更新)
令f[i][j][k]表示添加了前i个数时,还有j个开放的集合,当前总代价和为k的方案数,那么对于当前所有开放的集合,每次转移的总累加量为j*(a[i+1]-a[i]),而转移则有4种:
1.将a[i]加入一个开放的集合,不闭合
2.将a[i]加入一个开放的集合并闭合它
3.用a[i]新开一个集合,不闭合
4.用a[i]新开一个集合并闭合它
另外,本题要用滚动数组,否则会爆空间
#include<bits/stdc++.h> using namespace std; const int MOD=1e9+7; int n,k,ans=0; int s[205],f[2][205][1005]; int main() { memset(f,0,sizeof(f)); int n,k; scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&s[i]); sort(s+1,s+1+n); int p1=0,p2=1; f[p1][1][0]=1; f[p1][0][0]=1; for(int i=1;i<=n-1;i++) { for(int j=0;j<=i;j++) for(int t=0;t<=k;t++) { if(f[p1][j][t]==0) continue; int dt=s[i+1]-s[i],val=f[p1][j][t]; f[p1][j][t]=0; if(t+j*dt>k) continue; f[p2][j][t+j*dt]=((f[p2][j][t+j*dt]%MOD)+((long long)j*val%MOD))%MOD; if(j-1>=0) f[p2][j-1][t+j*dt]=((f[p2][j-1][t+j*dt]%MOD)+((long long)j*val%MOD))%MOD; f[p2][j+1][t+j*dt]=(f[p2][j+1][t+j*dt]%MOD+val%MOD)%MOD; f[p2][j][t+j*dt]=(f[p2][j][t+j*dt]%MOD+val%MOD)%MOD; } swap(p1,p2); } for(int i=0;i<=k;i++) ans=(ans%MOD+f[p1][0][i]%MOD)%MOD; printf("%d",ans); return 0; }