BZOJ 3971 Матрёшка 解题报告
很自然想到区间 DP。
设 $Dp[i][j]$ 表示把区间 $[i, j]$ 内的套娃合并成一个所需要的代价,那么有:
- $Dp[i][i] = 0$
- $Dp[i][j] = min\{Dp[i][k] + Dp[k + 1][j] + Merge([i, k], [k + 1, j])\} (i \le k < j)$
于是问题在于算 $Merge([a, b], [c, d])$。
我们考虑一下:区间 $[a, b]$ 内的哪些套娃是需要打开的:
是不是 $[a, b]$ 中所有大于 $[c, d]$ 中最小的套娃都需要打开,来装 $[c, d]$ 中最小的套娃呢?
$[c, d]$ 同理。
于是我们可以预处理 $Sum[i][x]$ 为在前 $i$ 个套娃中,大小 $\le x$ 的套娃的个数,
那么就可以 $O(1)$ 地算 $Merge([a, b], [c, d])$ 了。
有一个问题,如果在 $[a, b]$、$[c, d]$ 中有相同大小的套娃怎么办?
不着急,先往下看。
处理完 $Dp[i][j]$ 后,我们再设立一个 $F[i]$ 表示把前 $i$ 个套娃装成若干个完好的套娃集所需代价的最小值,那么就有:
- $F[i] = min(F[j] + Dp[j + 1][i]) (mex([j + 1, i]) = i - j + 1)$
- $mex([u, v]) = min(x) (x\notin \{A_u, A_{u+1}, \dots, A_v\})$
当然,如果不能找到合法的转移点,那么令 $F[i] = INF$。
然后对于一个合法的方案,不可能出现相同大小的套娃被装进同一个套娃内的情况,
所以 $[a, b]$、$[c, d]$ 中有相同大小的套娃的话,那么这个区间一定不合法,所以我们大可不必考虑那么多了。
于是最后看 $F[n]$ 就可以了。令 $M = max\{A_i\}$
时间复杂度 $O(n^3 + n^2M)$,空间复杂度 $O(n^2 + nM)$。
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 #define N 500 + 5 8 #define INF 593119681 9 10 int n, Max; 11 int A[N], _Dp[N], T[N]; 12 int Sum[N][N], Dp[N][N], Mex[N][N], Min[N][N]; 13 14 inline void Prepare() 15 { 16 for (int i = 1; i <= n; i ++) 17 Sum[i][A[i]] ++; 18 for (int i = 1; i <= n; i ++) 19 for (int j = 1; j <= Max; j ++) 20 Sum[i][j] += Sum[i][j - 1] + Sum[i - 1][j] - Sum[i - 1][j - 1]; 21 22 for (int i = 1; i <= n; i ++) 23 { 24 for (int j = 1; j <= Max; T[j ++] = 0) ; 25 for (int j = i; j <= n; j ++) 26 { 27 T[A[j]] ++; 28 for (Mex[i][j] = 1; T[Mex[i][j]]; Mex[i][j] ++) ; 29 Min[i][j] = i == j ? A[i] : min(Min[i][j - 1], A[j]); 30 } 31 } 32 } 33 34 inline int Calc(int l, int mid, int r) 35 { 36 int min_1 = Min[l][mid], res = Sum[r][Max] - Sum[mid][Max] - Sum[r][min_1] + Sum[mid][min_1]; 37 min_1 = Min[mid + 1][r], res += Sum[mid][Max] - Sum[l - 1][Max] - Sum[mid][min_1] + Sum[l - 1][min_1]; 38 return res; 39 } 40 41 int main() 42 { 43 #ifndef ONLINE_JUDGE 44 freopen("3971.in", "r", stdin); 45 freopen("3971.out", "w", stdout); 46 #endif 47 48 scanf("%d", &n); 49 for (int i = 1; i <= n; i ++) 50 { 51 scanf("%d", A + i); 52 Max = max(Max, A[i]); 53 } 54 Prepare(); 55 for (int len = 0; len < n; len ++) 56 for (int s = 1; s + len <= n; s ++) 57 { 58 int i = s, j = s + len; 59 if (i == j) Dp[i][j] = 0; 60 else 61 { 62 Dp[i][j] = INF; 63 for (int k = i; k < j; k ++) 64 Dp[i][j] = min(Dp[i][j], Dp[i][k] + Dp[k + 1][j] + Calc(i, k, j)); 65 } 66 } 67 for (int i = 1; i <= n; i ++) _Dp[i] = INF; 68 for (int i = 1; i <= n; i ++) 69 for (int j = 0; j < i; j ++) 70 if (Mex[j + 1][i] == i - j + 1) 71 _Dp[i] = min(_Dp[i], _Dp[j] + Dp[j + 1][i]); 72 if (_Dp[n] >= INF) puts("Impossible"); 73 else printf("%d\n", _Dp[n]); 74 75 #ifndef ONLINE_JUDGE 76 fclose(stdin); 77 fclose(stdout); 78 #endif 79 return 0; 80 }