Codeforces 626F Group Projects (DP)
题目链接 F.Group Projects
题意 把 \(n\) 个物品分成若干组,每个组的代价为组内价值的极差,求所有组的代价之和不超过 \(k\) 的方案数。
考虑 DP, \(f[i][j][k]\) 表示考虑到第 \(i\) 个物品的时候,还有 \(j\) 组尚未分配完毕,当前状态总代价为 \(k\) 的方案数
先把 \(a[]\) 升序排列,那么极差就可以转化为后面元素减前面的元素不听叠加的效果。
当考虑第 \(i\) 个物品的时候有 \(4\) 种转移方法 :
- 当前物品新开一组并且等待分配
- 当前物品新开一组并且这个物品单独当做一组
- 当前物品插入到之前的 \(j\) 组中的一组,并让这个组继续等待分配,那么有 \(j\) 种插入的方案
- 当前物品插入到之前的 \(j\) 组中的一组,并作为这个组的最大值(停止分配),同样有 \(j\) 种插入的方案
时间复杂度 \(O(n^2k)\)
view code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define LL long long
#define rep(i, a, b) for(int i(a); i <= b; ++ i)
#define dec(i, a, b) for(int i(a); i >= b; -- i)
//mt19937 mrand(random_device{}());
//int rnd(int x) { return mrand() % x;}
template <typename T> void chkmax(T &x, T y) { x = x >= y ? x : y; }
template <typename T> void chkmin(T &x, T y) { x = x <= y ? x : y; }
template <typename T> T abs(T x) {return x >= 0 ? x : -x;}
const int N = 202, M = 1010, MOD = 1e9 + 7;
int n, m;
int a[N];
LL f[2][N][M];
LL ans;
void add(LL &x, LL y) {
x += y;
while(x >= MOD) {
x -= MOD;
}
}
int main() {
scanf("%d%d", &n, &m);
rep(i, 1, n) {
scanf("%d", a + i);
}
std::sort(a + 1, a + 1 + n);
f[0][0][0] = 1;
//f[i][j][k] 表示 在前 i 个物品中选, 且剩余 j 个背包没有填完 并且 不平衡值 为 k
rep(i, 0, n - 1) {
std::memset(f[i + 1 & 1], 0, sizeof f[i + 1 & 1]);
rep(j, 0, n) {
rep(k, 0, m) { if(f[i & 1][j][k] && k + j * (a[i + 1] - a[i]) <= m) {
int cnt = k + j * (a[i + 1] - a[i]);
add(f[i + 1 & 1][j + 1][cnt], f[i & 1][j][k]); //新开一个背包 并且这个背包没有填完
add(f[i + 1 & 1][j][cnt], f[i & 1][j][k]); //新开一个背包 并且这个背包填完了
//放入之前的背包
if(j) {
add(f[i + 1 & 1][j][cnt], f[i & 1][j][k] * j % MOD); //没有填完任何一个背包
add(f[i + 1 & 1][j - 1][cnt], f[i & 1][j][k] * j % MOD); //填完一个背包
}
}
}
}
}
rep(i, 0, m) add(ans, f[n & 1][0][i]);
printf("%lld", ans);
return 0;
}