CF626F Group Projects

CF626F Group Projects

题意

题目大意已经说得很清楚了

题解

神仙套路 DP?????
首先不考虑顺序的话就是直接把原数组从小到大排个序
然后考虑DP就相当于是括号匹配

好像有点抽在这里插入图片描述

左括号的代价就是负的,然后右括号的代价就是正的

f [ i ] [ j ] [ k ] i j , k 设f[i][j][k]表示前i个数,还有j个没有被匹配,代价为k的方案数
转移十分简单

  • f [ i + 1 ] [ j + 1 ] [ k a [ i ] ] + = f [ i ] [ j ] [ k ] f[i+1][j+1][k -a[i]] +=f[i][j][k] 作为左括号(集合中最小的那个数)
  • f [ i + 1 ] [ j 1 ] [ k + a [ i ] ] + = f [ i ] [ j ] [ k ] j ( ) f[i+1][j-1][k+a[i]]+=f[i][j][k]*j 匹配掉一个左括号(作为集合中最大的那个数)
  • f [ i + 1 ] [ j ] [ k ] + = f [ i ] [ j ] [ k ] ( j + 1 ) f[i+1][j][k] += f[i][j][k]*(j+1) 作为前面的一个未被匹配的集合的中间值或者自己成为一组

答案就是 f [ n ] [ 0 ] [ 0... k ] \sum f[n][0][0...k]
然后可以发现这个转移是 O ( 1 ) O(1) 的,但是状态数是 O ( n 2 a [ i ] ) O(n^2 \sum a[i])
发现直接GG了

通过题目可以发现 k < = 1000 k<=1000 也就是说最后状态代价最多只有 0 1000 0\sim1000 是有用的,但是上面转移 k k 的范围是 a [ i ] a [ i ] -\sum a[i] \sim \sum a[i] 会逝世


正解

考虑差分,那原来最大值减最小值就可以转换为差分数组的一段的和

那样就只有加没有减了

k 转移最后一维大于k的就可以丢掉了
考虑转移

和上面的差不多,比较麻烦的就是代价的计算

就是当前差分乘上还没有被匹配的集合的个数
n x t = k + ( a [ i + 1 ] a [ i ] ) j nxt = k +(a[i+1] - a[i]) *j

然后转移就和账面一毛一个样了

code:


#include<bits/stdc++.h>
#define ll long long
#define N 505
#define mod 1000000007
using namespace std;
void Add(int &x, int y) {
	x += y;
	if(x >= mod) x-= mod;
}
int n, m, dp[2][N][N << 1], a[N];
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[0][0][0] = 1;
	for(int i = 0; i < n; i ++) {
		int fr = i & 1, to = fr ^ 1;
		memset(dp[to], 0, sizeof dp[to]);
		for(int j = 0; j <= n; j ++) {
			for(int k = 0; k <= m; k ++) {
				
				int nxt = k; Add(nxt, (ll)(a[i + 1] - a[i]) * j);
				if(nxt > m || !dp[fr][j][k]) continue;//printf("%d %d %d %d  %d %d\n", fr, to, j, k, nxt, i);
				Add(dp[to][j + 1][nxt], dp[fr][j][k]); // zi li yi zu
				if(j) Add(dp[to][j - 1][nxt], (ll)dp[fr][j][k] * j % mod);// xiao yi zu
				Add(dp[to][j][nxt], (ll)dp[fr][j][k] * (j + 1) % mod); // bu xuan huo zuo wei zhong jian zhi
			}
		}
	}
	int ans = 0;
	for(int i = 0; i <= m; i ++) Add(ans, dp[n & 1][0][i]);
	printf("%d", ans);
	return 0; 
}

总结

我写DP像***

对于这种DP要把状态巧妙转化一下
转化的方法一般有

  • 把贡献拆开
  • 差分
  • 看题解(呸
发布了157 篇原创文章 · 获赞 90 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_38944163/article/details/102974684