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
", 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 }
原文地址:https://www.cnblogs.com/gromah/p/4424405.html