题解:P12349 [蓝桥杯 2025 省 B 第二场] 翻转硬币
把真实思考没想出来和看题解的过程总结了一下,希望对你有帮助。
题目大意
- 每个硬币的价值是其在
n\times m 的矩阵内四方向相邻的硬币中与它正反相同的硬币个数的平方。 - 可以选择翻转任意多的行以最大化总价值。
-
状态定义
首先这是一个 DP。如果设
接下来我们发现第
正如之前所说,需要有状态机来确定靠近 当且仅当这题一定是 DP。所以最终我们得出:设
表示在第
状态转移
在分析状态的时候我们已经提到,需要枚举三行的状态来转移,也就是从
其中
代码实现与细节
#include <bits/stdc++.h>
using namespace std;
const int N = 1005;
int n, m, a[N][N];
int dp[N][2][2];
int q(int i, bool x, bool y, bool z)
{
int sum = 0;
for (int j = 1; j <= m; j++) {
int now = 0;
if (j > 1 && a[i][j - 1] == a[i][j]) {
now++;
}
if (j < m && a[i][j + 1] == a[i][j]) {
now++;
}
if (i > 1) {
if (x ^ y) { // i和i-1状态不同
if (a[i - 1][j] != a[i][j]) {
now++;
}
} else {
if (a[i - 1][j] == a[i][j]) {
now++;
}
}
}
if (i < n) {
if (y ^ z) {
if (a[i + 1][j] != a[i][j]) {
now++;
}
} else {
if (a[i + 1][j] == a[i][j]) {
now++;
}
}
}
sum += now * now;
}
return sum;
}
signed main()
{
cin.tie(0)->sync_with_stdio(0);
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
char x;
cin >> x;
a[i][j] = x - '0';
}
}
for (int i = 2; i <= n + 1; i++) {
dp[i][0][0] = max(dp[i - 1][0][0] + q(i - 1, 0, 0, 0), dp[i - 1][0][1] + q(i - 1, 1, 0, 0));
dp[i][0][1] = max(dp[i - 1][1][0] + q(i - 1, 0, 1, 0), dp[i - 1][1][1] + q(i - 1, 1, 1, 0));
dp[i][1][0] = max(dp[i - 1][0][1] + q(i - 1, 1, 0, 1), dp[i - 1][0][0] + q(i - 1, 0, 0, 1));
dp[i][1][1] = max(dp[i - 1][1][0] + q(i - 1, 0, 1, 1), dp[i - 1][1][1] + q(i - 1, 1, 1, 1));
}
cout << max({dp[n + 1][0][0], dp[n + 1][0][1], dp[n + 1][1][0], dp[n + 1][1][1]});
return 0;
}
- 注意你的
q 函数定义,在状态转移中要把每行的状态对应上,不要对应错。 - 如果你要压行,
q 函数的异或判断可以用位运算去掉,但是实际没必要。 - 总结:如果
i 的价值和前后都有关系,设dp_i 是前i-1 个元素的最大总价值,然后枚举相邻3 个元素的状态进行更新。
AC 记录,完结撒花!若本题解有助于君,恳留一赞!