洛谷 P3558 [POI2013]BAJ-Bytecomputer

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

思路

挺神的一道线性 ( 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) 的方案数:因为当前数已经是 (-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}))

综上转移方程汇总:

[ 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;
}
原文地址:https://www.cnblogs.com/loceaner/p/13692720.html