0%

CF Edu 189 C - Red-Black Pairs

比赛Educational Codeforces Round 189 (Rated for Div. 2) · 题目链接C. Red-Black Pairs

题目大意

题面

有一张 2×n2 \times n 的格子表,每个格子涂红色 ® 或黑色 (B)。要重涂最少多少个格子,使得能把全部格子分成 nn 对,满足:

  • 每对两个格子 同色

  • 每对两个格子 共边相邻

输出最少重涂次数。

输入格式

第一行 tt1t1041 \le t \le 10^4 )组测试。

每组:

  • 第 1 行: nn1n21051 \le n \le 2 \cdot 10^5

  • 第 2、3 行:长度为 nn 的字符串,描述上下两行的颜色

约束: n2105\sum n \le 2 \cdot 10^5

样例

输入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
5
1
R
B
2
BR
BR
3
RBR
BRB
4
RRBB
BBRB
5
RBRBR
BBBRB

输出:

1
2
3
4
5
1
0
3
1
4

第 4 个样例一种合法重涂是

1
2
RRBB
BBRR

思路讲解

一句话

每对配对就是一块 1×21 \times 2 的多米诺骨牌; 2×n2 \times n 网格用骨牌完整铺满只有两种局部砖型——整列竖砖相邻两列上下各一块横砖。线性 DP 一遍扫过去就行, O(n)O(n)

关键观察:配对结构 ≡ 2×n 多米诺铺法

题目要求「每对同色且相邻」。把「相邻」这一几何约束抽出来看,每对就是一块 1×21 \times 2多米诺骨牌。问题立刻翻译成:

2×n2 \times n 的网格用 nn1×21 \times 2 多米诺骨牌完全铺满。每块骨牌覆盖两个格子,要求两格同色——不同色就重涂一格,cost = 1。

2×n2 \times n 网格的多米诺铺法是经典结构:任意一种铺法,每一列要么是一块整列竖砖,要么和相邻列形成「上下两块横砖」的组合

横砖一定 成对出现——上行的横砖跨第 ii 到第 i+1i+1 列时,下行同 iii+1i+1 列也必须放一块横砖;不能上行横、下行竖混搭,否则下行第 ii 列就剩一个孤立格子接不上。

殊途同归地,原题的「重涂使可配对」就成了:枚举所有 2×n 多米诺铺法,选总 cost 最低那一种。铺法数线性可枚举,于是 DP。

DP 转移

dp[i]\mathrm{dp}[i] = 前 ii 列已经合法铺完的最少重涂次数。两种转移:

1) 第 ii 列放整列竖砖:cost = (上下两格不同色 ? 1 : 0)

dp[i]  =  dp[i1]+[s0[i]s1[i]]\mathrm{dp}[i] \;=\; \mathrm{dp}[i-1] + \bigl[\,s_0[i] \ne s_1[i]\,\bigr]

2) 第 i1,ii-1, i 两列各放一块横砖(上下行各一块):上行 cost + 下行 cost

dp[i]  =  dp[i2]+[s0[i1]s0[i]]+[s1[i1]s1[i]]\mathrm{dp}[i] \;=\; \mathrm{dp}[i-2] + \bigl[\,s_0[i-1] \ne s_0[i]\,\bigr] + \bigl[\,s_1[i-1] \ne s_1[i]\,\bigr]

两种转移取 min。边界 dp[0]=0\mathrm{dp}[0] = 0 (空网格已铺好),答案 dp[n]\mathrm{dp}[n]

验证:样例 5

n=5n = 5 ,上行 s0s_0 = RBRBR ,下行 s1s_1 = BBBRB

i 竖砖 cost 横砖 cost (上+下) dp[i]
1 R≠B → 1 0 + 1 = 1
2 B = B → 0 R≠B + B = B → 1 + 0 min(1+0, 0+1) = 1
3 R≠B → 1 B≠R + B = B → 1 + 0 min(1+1, 1+1) = 2
4 B≠R → 1 R≠B + B≠R → 1 + 1 min(2+1, 1+2) = 3
5 R≠B → 1 B≠R + R≠B → 1 + 1 min(3+1, 2+2) = 4

输出 4 ✓

复杂度

时间 O(n)O(n) ,空间 O(n)O(n) (dp 数组其实只用最近两项可以滚到 O(1)O(1) ,但 n2105n \le 2 \cdot 10^5 完全没必要省)。

AC 代码

AC 提交:373262009

核心 Solve() 就 7 行有效代码,DP 部分直接照公式翻译:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Solve() {
ll N;
cin >> N;
vector<array<char, 2> > s(N);
for (int i = 0; i < N; ++i) cin >> s[i][0];
for (int i = 0; i < N; ++i) cin >> s[i][1];
vector<ll> dp(N + 1, INF);
dp[0] = 0;
for (int i = 0; i < N; ++i) {
dp[i + 1] = min(dp[i + 1], dp[i] + (s[i][0] != s[i][1]));
if (i >= 1) {
dp[i + 1] = min(dp[i + 1], dp[i - 1] + (s[i][0] != s[i - 1][0]) + (s[i][1] != s[i - 1][1]));
}
}
cout << dp[N] << "\n";
}

心路历程(贪心 → DP)

第一版:邻居贪心(弃用)

刚开始想的是 局部贪心:扫每个格子,看它上下左右四个邻居有没有同色的——如果一个同色邻居都没有,就把它强制改成某个异色邻居的颜色,ans++。

代码挂在 C_Red_Black_Pairs.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
for (int x = 0; x < 2; ++x) {
for (int y = 0; y < N; ++y) {
ll cnt = 0; char ch;
for (int i = 0; i < 4; ++i) {
ll tox = x + dx[i], toy = y + dy[i];
if (check(tox, toy)) {
if (s[tox][toy] == s[x][y]) ++cnt;
else ch = s[x][y];
}
}
if (!cnt) { s[x][y] = ch; ++ans; }
}
}

这个贪心 是错的。问题在于:

  • 局部把一个孤立格子改色并不保证全局能成功配对;

  • 「改 1 格」有时不如「换一种铺法多改 0 格但解锁后续更便宜的配对」。

第二版:DP(AC)

意识到结构本质就是 2×n 多米诺骨牌铺满 之后,DP 的状态和转移就一目了然——只剩两种砖型,按列扫一遍,AC(373262009)。

教训

看到「两行 / 网格配对」时的第一反应应该是 多米诺铺法,而不是「邻居贪心」——前者是结构化的(铺法只有两种局部规则),后者是临时拼凑的,没法保证全局最优。

附件

暂无(题目较短,没写 mentor.tex 录稿;动画也没做)。