一、题目
1、题目描述
2、输入输出
2.1输入
2.2输出
3、原题链接
D - Grid Puzzle
二、解题报告
1、思路分析
贪心做法看不懂(为什么我赛时要跟贪心过不去啊)
这个题麻烦在这个case:2 4 4 2,我们可以清除三次2x2得到
但是我们始终有一个原则:相邻两行一定是两次操作以内完成的,不可能有3次清除2x2来清除相邻两行的存在,否则我们可以两次清行来替换
这也是为什么我一直死磕贪心,没写状压
其实状压的话就很经典了,比蒙德里安的理想要简单
我们考虑这样定义状态 f(i, j), j = 0, 1, 2, 3
f(i, 0):清除前i行,第i行清行(注意这个状态定义清行可以继承前面的2x2操作)
f(i, 1):清除前i行,第i行在1、2两个格子清空2x2(会对下一行有影响)
f(i, 2):清除前i行,第i行在3、4两个格子清空2x2(会对下一行有影响)
f(i, 3):清除前i行,第i行在1、2 | 3、4分别清空2x2(会对下一行有影响)
状态转移:
如果a[i] > 4 以及 不考虑前面2x2操作的情况下
f[i][0] = min(f[i - 1]) + 1
如果 a[i] <= 4,那么我们就要考虑2x2了:
f[i + 1][0] = min(f[i + 1][0], f[i][3]);
f[i + 1][1] = min(f[i + 1][1], f[i][2] + 1);
f[i + 1][2] = min(f[i + 1][2], f[i][1] + 1);
f[i + 1][3] = min(f[i + 1][3], f[i][0] + 2);
如果 a[i] <= 2:
f[i + 1][0] = min(f[i + 1][0], f[i][1]);
f[i + 1][1] = min(f[i + 1][1], f[i][0] + 1);
然后特判 a[i] == 0的情况,f[i][0] = min(f[i - 1])
2、复杂度
时间复杂度: O(4N)空间复杂度:O(4N)
3、代码详解
#include <bits/stdc++.h>
#define sc scanf
using i64 = long long;
using PII = std::pair<int, int>;
constexpr int inf32 = 1e9 + 7;
constexpr i64 inf64 = 1e18 + 7;void chmin(int& x, int y) {x = y < x ? y : x;
}void solve() {int n;std::cin >> n;std::vector<int> a(n);for (int i = 0; i < n; ++ i) std::cin >> a[i];std::vector<std::array<int, 4>> f(n + 1, { inf32, inf32, inf32, inf32 });f[0][0] = 0;for (int i = 0; i < n; ++ i) {f[i + 1][0] = *std::min_element(f[i].begin(), f[i].end()) + 1;if (a[i] <= 4) {chmin(f[i + 1][0], f[i][3]);chmin(f[i + 1][1], f[i][2] + 1);chmin(f[i + 1][2], f[i][1] + 1);chmin(f[i + 1][3], f[i][0] + 2);}if (a[i] <= 2) {chmin(f[i + 1][0], f[i][1]);chmin(f[i + 1][1], f[i][0] + 1);}if (a[i] == 0)chmin(f[i + 1][0], *std::min_element(f[i].begin(), f[i].end()));}std::cout << *std::min_element(f[n].begin(), f[n].end()) << '\n';
}int main() {
#ifdef DEBUGfreopen("in.txt", "r", stdin);freopen("out.txt", "w", stdout);
#endifstd::ios::sync_with_stdio(false), std::cin.tie(nullptr), std::cout.tie(nullptr);int _ = 1;std::cin >> _;while (_ --)solve();return 0;
}