[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 初稿

posted @ 2023-01-07 15:48  Tsawke  阅读(15)  评论(0编辑  收藏  举报