Forever Young

洛谷 P3558 [POI2013]BAJ-Bytecomputer

被一篇我自认为写的挺好的题解给搞蒙了。
然后急中生智发现他写的很多状态都很冗余,改简洁之后就理解了

思路

挺神的一道线性 \(\text{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\) 的方案数:因为当前数已经是 \(-1\) 了,所以 \(f_{i,0}=f_{i-1,0}\)
    2. 变为 \(0\) 的方案数:如果要让 \(-1\) 变成 \(0\),前一个数必须是 \(1\),如果要 \(a_{i-1}\) 转化为 \(1\),那么当前数也必须是 \(1\),这样的话当前数就不能为 \(0\) 了,也就是说不能有 \(f_{i,1}\) 了,所以\(f_{i,1}=inf\)
    3. 变为 \(1\) 的方案数:同上,如果要让 \(-1\) 变成 \(1\),前一个数必须是 \(1\),转移次数为 \(2\),所以此时的方案数为 \(f_{i,2}=f_{i-1,2}+2\)
  • \(a_i=0\) 时:

    1. 变为 \(-1\) 的方案数:只能从上一个数变为 \(-1\) 的方案转移过来,转移次数为 \(1\),即 \(f_{i,0}=f_{i-1,0}+1\)
    2. 变为 \(0\) 的方案数:当前数已经为 \(0\) 了,上一个数是 \(-1,0\) 都行,所以取上一个数两个方案数中的最小值即可,即 \(f_{i,1}=\min(f_{i-1,0},f_{i-1,1})\)
    3. 变为 \(1\) 的方案数:同 \(a_i=-1\) 时的情况,之不管转移次数变成了 \(1\), 如果要让 \(0\) 变成 \(1\),那么前一个数需要是 \(1\),所以 \(f_{i,2}=f_{i-1,2}+1\)
  • \(a_i=1\) 时:

    1. 变为 \(-1\) 的方案数:只能从上一个数变为 \(-1\) 的方案转移过来,转移次数为 \(2\),即 \(f_{i,0}=f_{i-1,0}+2\)
    2. 变为 \(0\) 的方案数:前一个数必须是 \(-1\) 才能变成 \(0\),所以 \(f_{i,1}=f_{i-1,0}+1\)
    3. 变为 \(1\) 的方案数:当前数已经为 \(1\) 了,上一个数是什么都行,所以直接取上一个数三个方案数中的最小值即可,即 \(f_{i,2}=\min(f_{i-1,0},f_{i-1,1},f_{i-1,2})\)

综上转移方程汇总:

\[\text{if } a_i=-1\begin{cases}f_{i,0}=f_{i-1,0}\\f_{i,1}=inf\\f_{i,2}=f_{i-1,2}+2\end{cases} \]

\[\text{if }a_{i}=0\begin{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}+1\end{cases} \]

\[\text{if }a_{i}=1\begin{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\n";
  else cout << ans << '\n';
  return 0;
}
posted @ 2020-09-18 18:07  Loceaner  阅读(139)  评论(0编辑  收藏  举报