G. Paint Charges
G. Paint Charges
A horizontal grid strip of cells is given. In the -th cell, there is a paint charge of size . This charge can be:
- either used to the left — then all cells to the left at a distance less than (from to inclusive) will be painted,
- or used to the right — then all cells to the right at a distance less than (from to inclusive) will be painted,
- or not used at all.
Note that a charge can be used no more than once (that is, it cannot be used simultaneously to the left and to the right). It is allowed for a cell to be painted more than once.
What is the minimum number of times a charge needs to be used to paint all the cells of the strip?
Input
The first line of the input contains an integer () — the number of test cases in the test. This is followed by descriptions of test cases.
Each test case is specified by two lines. The first one contains an integer () — the number of cells in the strip. The second line contains positive integers (), where is the size of the paint charge in the -th cell from the left of the strip.
It is guaranteed that the sum of the values of in the test does not exceed .
Output
For each test case, output the minimum number of times the charges need to be used to paint all the cells of the strip.
Example
input
13
1
1
2
1 1
2
2 1
2
1 2
2
2 2
3
1 1 1
3
3 1 2
3
1 3 1
7
1 2 3 1 2 4 2
7
2 1 1 1 2 3 1
10
2 2 5 1 6 1 8 2 8 2
6
2 1 2 1 1 2
6
1 1 4 1 3 2
output
1
2
1
1
1
3
1
2
3
4
2
3
3
Note
In the third test case of the example, it is sufficient to use the charge from the -st cell to the right, then it will cover both cells and .
In the ninth test case of the example, you need to:
- use the charge from the -rd cell to the left, covering cells from the -st to the -rd;
- use the charge from the -th cell to the left, covering cells from the -th to the -th;
- use the charge from the -th cell to the left, covering cells from the -th to the -th.
In the eleventh test case of the example, you need to:
- use the charge from the -th cell to the right, covering cells from the -th to the -th;
- use the charge from the -th cell to the left, covering cells from the -st to the -th.
解题思路
官方题解给出的 dp 状态定义不好直接想到,这里先给出容易想到但有问题的做法,然后再引出官方题解的做法。
定义 表示所有由前 个位置将前缀 都涂上色的方案中操作次数的最小值。根据对第 个位置执行的操作将 转移到 对应的状态。
- 如果第 个位置不执行操作,那么显然有 。
- 如果第 个位置执行向左涂色的操作,只有当 ,有 。
- 如果第 个位置执行向右涂色的操作,只有当 ,有 。
最后的答案就是 。如果你按照这个做法来做那么就会发现题目倒数第 个样例跑出的答案是 ,实际上应该选择第 个位置向右涂色,第 个位置向左涂色。由于状态的定义要保证前缀被涂色,因此枚举到 时,位置 没被涂色而从位置 向右涂色这种状态是不存在的。
因此可以知道在枚举 的过程中不一定要保证前缀被涂色,同时被涂色的区间不一定只有连续的一段。对于这样的状态,就可以引出官方题解中给出的状态定义,即只关心此时最靠左没被涂色的位置,和最靠右已被涂色的位置。
定义 表示所有由前 个位置使得最靠左没被涂色的位置为 ,最靠右已涂色的位置为 的方案中操作次数的最小值。状态转移方程为:
- 如果第 个位置不执行操作,那么显然有 。
- 如果第 个位置执行向左涂色的操作,分两种情况。如果 ,则涂色后最靠左没被涂色的位置还是 ,最靠右被涂色的位置取决于 和 。因此有 。否则如果 ,继续分两种情况:,显然涂色后最靠左没被涂色的位置为 ,最靠右已涂色的位置为 ,因此有 。,由于是通过前 个位置使得第 个位置被涂色,因此区间 一定都被涂色,所以涂色后最靠左没被涂色的位置为 ,最靠右已涂色的位置为 ,有 。两种情况综合一下就有 。
- 如果第 个位置执行向右涂色的操作,同理分两种情况。如果 ,则有 。否则如果 ,则有 。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 110;
int a[N];
int f[N][N][N];
void solve() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
}
memset(f, 0x3f, sizeof(f));
f[0][1][0] = 0;
for (int i = 0; i < n; i++) {
for (int j = 1; j <= n + 1; j++) {
for (int k = 0; k <= n; k++) {
f[i + 1][j][k] = min(f[i + 1][j][k], f[i][j][k]);
if (j < i + 1 - a[i + 1] + 1) {
int &v = f[i + 1][j][max(k, i + 1)];
v = min(v, f[i][j][k] + 1);
}
else {
int &v = f[i + 1][max(k, i + 1) + 1][max(k, i + 1)];
v = min(v, f[i][j][k] + 1);
}
if (j < i + 1) {
int &v = f[i + 1][j][min(n, max(k, i + a[i + 1]))];
v = min(v, f[i][j][k] + 1);
}
else {
int &v = f[i + 1][min(n, max(k, i + a[i + 1])) + 1][min(n, max(k, i + a[i + 1]))];
v = min(v, f[i][j][k] + 1);
}
}
}
}
printf("%d\n", f[n][n + 1][n]);
}
int main() {
int t;
scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
参考资料
Codeforces Round 923 (Div. 3) Editorial:https://codeforces.com/blog/entry/125597
Codeforces Round 923 (Div. 3) A-G:https://zhuanlan.zhihu.com/p/681725397
Codeforces Round 923 (Div. 3) A - G:https://zhuanlan.zhihu.com/p/681721421
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18012597
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-02-09 D. Lucky Permutation
2022-02-09 拖拉机