BZOJ 3982 Stacking Plates 解题报告
我们首先可以得到:如果有一堆盘子里有一些相邻的盘子的直径相等,那么自然这些盘子可以统一处理,就可以缩成一个了。
然后我们接着考虑给每一堆盘子都染上一种颜色,那么操作的次数 step = diff * 2 - n + 1
其中 diff 表示最终的盘子堆中相邻的盘子的颜色不同的对数。
接着我们可以将盘子的直径离散化。
那么我们可以考虑Dp,设 Dp[s][i] 为处理完所有盘子直径小于等于 s 的盘子,并且最底下的盘子的颜色是 i 的 diff 的最小值。
至于转移的话呢,记直径为 s 的盘子个数为 tot[s],然后找到所有直径为 s 的盘子及其颜色 i ,那么就有:
- res1 = min(Dp[s - 1][j] + tot[s]) (j = 1 ~ n)
- res2 = min(Dp[s - 1][k] + tot[s] - 1) (存在一个直径为 s,颜色为 k 的盘子)
- Dp[s][i] = min(res1, res2)
初始化 Dp[0][i] = 0 (i = 1 ~ n)
答案 ans = min(Dp[Max_s][i]) * 2 - n + 1
于是就做完啦~
毕竟 Gromah 太弱,只会做水题。
1 #include <cmath> 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <algorithm> 6 using namespace std; 7 #define N 50 + 5 8 #define M 2500 + 5 9 #define SIZE 10000 + 5 10 #define INF 593119681 11 12 int _, n; 13 int A[N][N]; 14 int Size[N], Point[N], T[N]; 15 int Dp[2][N]; 16 int S[SIZE]; 17 18 inline void Init() 19 { 20 memset(S, 0, sizeof(S)); 21 for (int i = 1; i <= n; i ++) 22 { 23 scanf("%d", Size + i); 24 for (int j = 1; j <= Size[i]; j ++) 25 { 26 scanf("%d", A[i] + j); 27 S[A[i][j]] = 1; 28 } 29 Size[i] = unique(A[i] + 1, A[i] + Size[i] + 1) - A[i] - 1; 30 Point[i] = 1; 31 } 32 for (int i = 1; i < SIZE; i ++) 33 S[i] += S[i - 1]; 34 for (int i = 1; i <= n; i ++) 35 for (int j = 1; j <= Size[i]; j ++) 36 A[i][j] = S[A[i][j]]; 37 } 38 39 inline void Solve() 40 { 41 printf("Case %d: ", ++ _); 42 for (int i = 0; i <= n; i ++) 43 Dp[0][i] = 0; 44 for (int i = 1; i <= S[SIZE - 1]; i ++) 45 { 46 T[0] = 0; 47 Dp[1][0] = INF; 48 for (int j = 1; j <= n; j ++) 49 { 50 if (A[j][Point[j]] == i) 51 T[++ T[0]] = j, Point[j] ++; 52 Dp[1][j] = INF; 53 } 54 for (int j = 1; j <= T[0]; j ++) 55 { 56 for (int k = 1; k <= T[0]; k ++) 57 { 58 if (j == k && T[0] > 1) continue ; 59 Dp[1][T[k]] = min(Dp[1][T[k]], Dp[0][T[j]] + T[0] - 1); 60 } 61 for (int k = 0; k <= n; k ++) 62 Dp[1][T[j]] = min(Dp[1][T[j]], Dp[0][k] + T[0]); 63 } 64 for (int j = 0; j <= n; j ++) 65 Dp[0][j] = Dp[1][j]; 66 } 67 int Min = INF; 68 for (int i = 0; i <= n; i ++) 69 Min = min(Min, Dp[0][i]); 70 printf("%d\n", Min * 2 - n + 1); 71 } 72 73 int main() 74 { 75 #ifndef ONLINE_JUDGE 76 freopen("3982.in", "r", stdin); 77 freopen("3982.out", "w", stdout); 78 #endif 79 80 while (scanf("%d", &n) == 1) 81 { 82 Init(); 83 Solve(); 84 } 85 86 #ifndef ONLINE_JUDGE 87 fclose(stdin); 88 fclose(stdout); 89 #endif 90 return 0; 91 }