CF1700F Puzzle 解题报告
CF1700F Puzzle 解题报告
CF1700F Puzzle - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题意
给定两个\(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;
}
本文来自博客园,作者:zjsqwq,转载请注明原文链接:https://www.cnblogs.com/zjsqwq/p/16442980.html

浙公网安备 33010602011771号