CF1700F Puzzle 解题报告

CF1700F Puzzle 解题报告

CF1700F Puzzle - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Problem - 1700F - Codeforces

题意

给定两个\(2 \times n\)\(01\) 矩阵 \(A\)\(B\),定义一次操作为交换 \(A\) 中任意两个相邻的位置中的值,输出使得 \(A = B\)的最小操作次数,如果无法使\(A = B\) 则输出 \(-1\)

Sol

\(A\)\(B\)都只有一行,则答案就是\(\sum|sum_i|\)\(sum_i\)为前\(i\)个位置\(A_i- B_i\)的前缀和)。

推广到\(2\)行,因为题目没有要求交换次序,所以我们可以从数组起始往后递推。

若将\((0, i)\)\(1\)挪下去,则\(sum_{0, i} -= 1, sum_{1, i} += 1\)

可以想到,若\(sum_{0, i}\)\(sum_{1, i}\)同号,那么这次交换是不需要的,可以放到\(i + 1\)处进行。

而若\(sum_{0, i}\)\(sum_{1, i}\)异号,我们交换\(k = min(sum_{0, i}, sum_{1, i})\)次,节省了\(k\)次。

因为题目中没有说一定要上下或左右交换,所以我们以上是进行了上下交换,即把原本 \(suma_{0, i} > sumb_{0, i}\)\(suma_{1, i} < sumb_{1, i}\)\(suma_{0, i} < sumb_{0, i}\)\(suma_{1, i} > sumb_{1, i}\) 的两波左右交换用一波上下交换代替,推广到\(k\),就是用\(k\)次上下交换代替了\(2k\)次左右交换。

而若以上操作不如左右交换优秀,那么在后面会再进行\(k\)次上下交换以补救。恰好最终贡献还是\(2k\),思想类似于 反悔自动机的差值相消思想

要注意这道题虽然 看起来人畜无害 ,但也是得开\(long long\)的,因为中间的累加约等于\(n ^ 2\)

Code

#include <bits/stdc++.h>

using namespace std;

using ll = long long;

const int MN = 4e5 + 5;

int n;
int a[2][MN], b[2][MN], dp[2][MN];

signed main() {
    ios::sync_with_stdio(false), cin.tie(0);
    cin >> n;
    int sum = 0;
    for (int i = 0; i <= 1; ++i)
        for (int j = 1; j <= n; ++j) cin >> a[i][j], sum += a[i][j];
    for (int i = 0; i <= 1; ++i)
        for (int j = 1; j <= n; ++j) cin >> b[i][j], sum -= b[i][j];
    if (sum != 0) {
        puts("-1");
        return 0;
    }
    int sum1 = 0, sum2 = 0;
    ll ans = 0;
    for (int i = 1; i <= n; ++i) {
        sum1 += a[0][i] - b[0][i];
        sum2 += a[1][i] - b[1][i];
        if (sum1 < 0 && sum2 > 0) {
            int k = min(-sum1, sum2);
            ans += k;
            sum1 += k, sum2 -= k;
        }
        else if (sum1 > 0 && sum2 < 0) {
            int k = min(sum1, -sum2);
            ans += k;
            sum1 -= k, sum2 += k;
        }
        ans += abs(sum1) + abs(sum2);
    }
    cout << ans << '\n';
    return 0;
}
posted @ 2022-07-04 15:02  zjsqwq  阅读(119)  评论(0)    收藏  举报