扯
被一篇我自认为写的挺好的题解给搞蒙了。
然后急中生智发现他写的很多状态都很冗余,改简洁之后就理解了
思路
挺神的一道线性 ( ext{DP}) 题。
发现最后的序列也是 (-1,0,1) 序列,因为改为别的值花费一定会变得更多。
设 (f_{i,j}) 表示前 (i) 个数字已经排好,第 (i) 个数字变为 (j) 的方案数,但是显然不能用负数下标,所以定义可以有一些修改。设 (f_{i,j}) 表示前 (i) 个数字已经排好,第 (i) 个数字变为 (j-1) 的方案数,那么能够用的数就是 (f_{i,0},f_{i,1},f_{i,2}),分别表示当前数为 (-1,0,1) 时的情况。
初始化整个 (f) 数组为 (inf),并且初始化 (f_{1,a_1+1}=0),因为第一个数为 (a_1) 的方案数显然为 (0)。
然后分情况讨论:
-
当 (a_i=-1) 时:
- 变为 (-1) 的方案数:因为当前数已经是 (-1) 了,所以 (f_{i,0}=f_{i-1,0})。
- 变为 (0) 的方案数:如果要让 (-1) 变成 (0),前一个数必须是 (1),如果要 (a_{i-1}) 转化为 (1),那么当前数也必须是 (1),这样的话当前数就不能为 (0) 了,也就是说不能有 (f_{i,1}) 了,所以(f_{i,1}=inf)。
- 变为 (1) 的方案数:同上,如果要让 (-1) 变成 (1),前一个数必须是 (1),转移次数为 (2),所以此时的方案数为 (f_{i,2}=f_{i-1,2}+2)。
-
当 (a_i=0) 时:
- 变为 (-1) 的方案数:只能从上一个数变为 (-1) 的方案转移过来,转移次数为 (1),即 (f_{i,0}=f_{i-1,0}+1)。
- 变为 (0) 的方案数:当前数已经为 (0) 了,上一个数是 (-1,0) 都行,所以取上一个数两个方案数中的最小值即可,即 (f_{i,1}=min(f_{i-1,0},f_{i-1,1}))。
- 变为 (1) 的方案数:同 (a_i=-1) 时的情况,之不管转移次数变成了 (1), 如果要让 (0) 变成 (1),那么前一个数需要是 (1),所以 (f_{i,2}=f_{i-1,2}+1)。
-
当 (a_i=1) 时:
- 变为 (-1) 的方案数:只能从上一个数变为 (-1) 的方案转移过来,转移次数为 (2),即 (f_{i,0}=f_{i-1,0}+2)。
- 变为 (0) 的方案数:前一个数必须是 (-1) 才能变成 (0),所以 (f_{i,1}=f_{i-1,0}+1)。
- 变为 (1) 的方案数:当前数已经为 (1) 了,上一个数是什么都行,所以直接取上一个数三个方案数中的最小值即可,即 (f_{i,2}=min(f_{i-1,0},f_{i-1,1},f_{i-1,2}))。
综上转移方程汇总:
[ ext{if } a_i=-1egin{cases}f_{i,0}=f_{i-1,0}\f_{i,1}=inf\f_{i,2}=f_{i-1,2}+2end{cases}
]
[ ext{if }a_{i}=0egin{cases}f_{i,0}=f_{i-1,0}+1\f_{i,1}=min(f_{i-1,0},f_{i-1,1})\f_{i,2}=f_{i-1,2}+1end{cases}
]
[ ext{if }a_{i}=1egin{cases}f_{i,0}=f_{i-1,0}+2\f_{i,1}=f_{i-1,0}+1\f_{i,2}=min(f_{i-1,0},f_{i-1,1},f_{i-1,2})end{cases}
]
最后的答案显然就是 (f_{n,0},f_{n,1},f_{n,2}) 中的最小值。
注意要判断无解情况。
时间复杂度 (O(n))。
代码
//*
Time: 18/09/20 15:26
Author: Loceaner
Description: 线性DP
*/
#include <cmath>
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int A = 1e6 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
int n, a[A], f[A][3];
signed main() {
n = read();
for (int i = 1; i <= n; i++) a[i] = read();
memset(f, inf, sizeof(f));
f[1][a[1] + 1] = 0;
for (int i = 2; i <= n; i++) {
if (a[i] == -1) {
f[i][0] = f[i - 1][0];
f[i][2] = f[i - 1][2] + 2;
}
if (a[i] == 0) {
f[i][0] = f[i - 1][0] + 1;
f[i][1] = min(f[i - 1][0], f[i - 1][1]);
f[i][2] = f[i - 1][2] + 1;
}
if (a[i] == 1) {
f[i][0] = f[i - 1][0] + 2;
f[i][1] = f[i - 1][0] + 1;
f[i][2] = min(min(f[i - 1][0], f[i - 1][1]), f[i - 1][2]);
}
}
int ans = min(min(f[n][0], f[n][1]), f[n][2]);
if (ans == inf) cout << "BRAK
";
else cout << ans << '
';
return 0;
}