题目大意
给定一个长度为 $n$ 的 01 串和一个正整数 $m$。你要进行一系列操作,将串变为有长度为 $m$ 的循环节的串。操作一共有两种:
- 将串的某一位反转。
- 将串的一个长度为 $k \times m$ 的前缀全部反转。其中 $1 \le k \le \frac{n}{m}$。
问至少进行几次操作。
数据范围:$n \le 300, m \le n$。
思路分析
我们令 $M = \lfloor \frac{n}{m} \rfloor$。
先考虑 $m$ 较大的情况。
发现第二种操作最多只有 $M$ 种,我们暴力枚举每个操作是否使用,然后对于操作后的串计算使用第一种操作最少的次数即可。复杂度看似是 $2^M \times n$ 的,但实际上可以通过 DFS 来优化到 $O(2^M \times m)$。
再考虑 $m$ 较小的情况。
发现最终得到的串最多只有 $2^m$ 种,我们枚举每个串,然后处理出那些位置需要被反转,再用 DP 来计算最少修改次数即可。当然,也可以对于 $2^m$ 个串整体进行一次 DP。注意为了无后效性,DP 要倒序做。时间复杂度 $O(2^m \times M)$。
所以,我们当 $m \ge \sqrt{n}$ 的时候用第一种方案,否则用第二种方案即可。总时间复杂度:
(为啥这个题 $n$ 不出到 $400$ 啊 QAQ)
代码实现
1 |
|
2 |
|
3 |
|
4 | using namespace std; |
5 | |
6 | const int maxn = 300, maxm = 1 << 18, inf = 2e9 + 1; |
7 | int n, m; |
8 | char s[maxn + 3]; |
9 | |
10 | namespace subtask1 { |
11 | int k, a[maxn + 3], c[maxn + 3][2], ans = maxn; |
12 | void dfs(int d, int sum) { |
13 | if (!d) { |
14 | for (int i = 1; i <= m; i++) { |
15 | sum += min(c[i][0], c[i][1]); |
16 | } |
17 | ans = min(ans, sum); |
18 | return; |
19 | } |
20 | for (int t = 0; t < 2; t++) { |
21 | a[d] = t; |
22 | for (int i = 1; i <= m; i++) { |
23 | c[i][s[(d - 1) * m + i] - '0' != t]++; |
24 | } |
25 | dfs(d - 1, sum + (a[d] != a[d + 1])); |
26 | for (int i = 1; i <= m; i++) { |
27 | c[i][s[(d - 1) * m + i] - '0' != t]--; |
28 | } |
29 | } |
30 | } |
31 | void main() { |
32 | k = n / m; |
33 | for (int i = m * k + 1; i <= n; i++) { |
34 | c[i - m * k][s[i] - '0']++; |
35 | } |
36 | dfs(k, 0); |
37 | printf("%d\n", ans); |
38 | } |
39 | } |
40 | |
41 | namespace subtask2 { |
42 | int k, dp[2][maxm + 3][2]; |
43 | int bit_cnt(int x) { |
44 | return __builtin_popcount(x); |
45 | } |
46 | int diff(int msk1, int msk2) { |
47 | return bit_cnt(msk1 ^ msk2); |
48 | } |
49 | void upd(int &x, int y) { |
50 | x > y ? x = y : 0; |
51 | } |
52 | void main() { |
53 | k = n / m; |
54 | int bot = 0; |
55 | for (int i = m * k + 1; i <= n; i++) { |
56 | bot = bot << 1 | (s[i] - '0'); |
57 | } |
58 | int cur = 0, lst = 1; |
59 | memset(dp[cur], 0x3c, sizeof(dp[cur])); |
60 | for (int msk = 0; msk < 1 << m; msk++) { |
61 | dp[cur][msk][0] = diff(bot, msk >> (m - n + m * k)); |
62 | } |
63 | for (int i = k; i; i--) { |
64 | swap(cur, lst); |
65 | memset(dp[cur], 0x3c, sizeof(dp[cur])); |
66 | int num = 0; |
67 | for (int j = 1; j <= m; j++) { |
68 | num = num << 1 | (s[(i - 1) * m + j] - '0'); |
69 | } |
70 | for (int msk = 0; msk < 1 << m; msk++) { |
71 | for (int j = 0; j < 2; j++) { |
72 | for (int t = 0; t < 2; t++) { |
73 | upd(dp[cur][msk][t], dp[lst][msk][j] + (j ^ t) + diff(num ^ (t ? (1 << m) - 1 : 0), msk)); |
74 | } |
75 | } |
76 | } |
77 | } |
78 | int ans = inf; |
79 | for (int msk = 0; msk < 1 << m; msk++) { |
80 | ans = min(ans, min(dp[cur][msk][0], dp[cur][msk][1])); |
81 | } |
82 | printf("%d\n", ans); |
83 | return; |
84 | } |
85 | } |
86 | |
87 | int main() { |
88 | scanf("%s %d", s + 1, &m); |
89 | n = strlen(s + 1); |
90 | if (m > 18) { |
91 | subtask1::main(); |
92 | } else { |
93 | subtask2::main(); |
94 | } |
95 | return 0; |
96 | } |