[ABC264F] Monochromatic Path 题解
[ABC264F] Monochromatic Path Solution
更好的阅读体验戳此进入
题面
给定一个 $ H $ 行 $ W $ 列的 $ 01 $ 矩阵。你可以花费 $ R_i $ 将矩阵第 $ i $ 行进行 $ 01 $ 反转,可以花费 $ C_j $ 将矩阵第 $ j $ 列进行 $ 01 $ 反转。你需要最小化花费,并使得从 $ (1, 1) $ 出发,只能向右或下走到达 $ (H, W) $ 至少有一条路径满足均为 $ 0 $ 或 $ 1 $。
Solution
首先不难想到每一行或一列最多进行一次反转操作,否则是无用的。发现只能向右或向下,则无后效性,故可以尝试 DP。
设 $ dp(i, j, 0/1, 0/1) $ 表示处理到第 $ i $ 行 $ j $ 列,第 $ i $ 行和第 $ j $ 列是否反转的最小花费。
我们设当前状态为 $ dp(i, j, x, y) $,令 $ G_{i, j} $ 表示矩阵的元素,则对于向下走的下一步的转移比较显然:
\[\begin{aligned}
dp(i, j, x, y) \rightarrow dp(i + 1, j, 0, y) &\quad G_{i, j} \oplus x \oplus y = G_{i + 1, j} \oplus y \\
dp(i, j, x, y) + R_{i + 1} \rightarrow dp(i + 1, j, 1, y) &\quad G_{i, j} \oplus x \oplus y \neq G_{i + 1, j} \oplus y
\end{aligned}
\]
对于向右走的下一步也同理可得。
初始值即为 $ dp(1, 1, 0, 0) \leftarrow 0, dp(1, 1, 1, 0) \leftarrow R_1, dp(1, 1, 0, 1) \leftarrow C_1, dp(1, 1, 1, 1) \leftarrow R_1 + C_1 $。
最终答案即为 $ \min{dp(H, W, x, y)} $。
Code
#define _USE_MATH_DEFINES
#include <bits/stdc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
using namespace std;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template < typename T = int >
inline T read(void);
int H, W;
ll R[2100], C[2100];
ll dp[2100][2100][2][2];
bitset < 2100 > G[2100];
int main(){
H = read(), W = read();
for(int i = 1; i <= H; ++i)R[i] = read();
for(int i = 1; i <= W; ++i)C[i] = read();
for(int i = 1; i <= H; ++i)
for(int j = 1; j <= W; ++j){
char c = getchar(); while(!isdigit(c))c = getchar();
G[i][j] = c == '1' ? true : false;
}
memset(dp, 0x3f, sizeof dp);
dp[1][1][0][0] = 0, dp[1][1][1][0] = R[1], dp[1][1][0][1] = C[1], dp[1][1][1][1] = R[1] + C[1];
for(int i = 1; i <= H; ++i)
for(int j = 1; j <= W; ++j)
for(int x = 0; x <= 1; ++x)
for(int y = 0; y <= 1; ++y){
if((G[i][j] ^ x ^ y )== (G[i + 1][j] ^ y))dp[i + 1][j][0][y] = min(dp[i + 1][j][0][y], dp[i][j][x][y]);
else dp[i + 1][j][1][y] = min(dp[i + 1][j][1][y], dp[i][j][x][y] + R[i + 1]);
if((G[i][j] ^ x ^ y) == (G[i][j + 1] ^ x))dp[i][j + 1][x][0] = min(dp[i][j + 1][x][0], dp[i][j][x][y]);
else dp[i][j + 1][x][1] = min(dp[i][j + 1][x][1], dp[i][j][x][y] + C[j + 1]);
}
ll ans(LONG_LONG_MAX);
for(int x = 0; x <= 1; ++x)for(int y = 0; y <= 1; ++y)ans = min(ans, dp[H][W][x][y]);
printf("%lld\n", ans);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template < typename T >
inline T read(void){
T ret(0);
int flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2023_01_03 初稿