ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P3558 [POI2013]BAJ-Bytecomputer

2020-11-12 10:34:23  阅读:184  来源: 互联网

标签:fir ch int POI2013 cdots le cases BAJ P3558


知识点:结论,线性 DP

原题面:Luogu


题意简述

给定一只包含 \(-1.0,1\) 的数列 \(a\),每次操作可令 \(a_{i} + a_{i-1}\)。
判断能否使该序列单调不降,若可以则求所需的最少操作次数。
\(1\le n\le 10^6\),\(|a_i|\le 1\)。


分析题意

发现一些结论:

  • 显然应该从前往后操作。
  • 若某位置为 \(1\),则经过若干次操作后它之后的位置都可以 \(\ge 1\)。
  • 若某位置为 \(-1\),则经过若干次操作后它之后的位置都可以 \(\le -1\)。
  • 由上述结论可知,若出现:\(0,\cdots, 0, -1,\cdots\) 的情况,则一定无解,否则一定有解。
  • 显然最终答案一定是 \(-1\cdots, -1,0,\cdots,0,1\) 的形式,不可能出现 \(|a_i|\ge 2\) 的情况。 因为 \(-2\) 只能通过 \(-1-1\) 得到,此时已经单调不降了,变为 \(-2\) 一定不优。\(2\) 同理。

根据结论 4 判下无解,由结论 5,考虑 DP。
设 \(f_{i,-1/0/1}\) 表示当前操作到第 \(i\) 个数,第 \(i\) 个数变为 \(-1/0/1\) 时,令前 \(i\) 个数单调不降的最小操作次数。
初始化 \(f_{1,a_1} = 0\),转移时分类讨论:

\[\begin{aligned} f_{i,-1} &= \begin{cases} f_{i-1,-1} &(a_i = -1)\\ f_{i-1,-1} + 1 &(a_i = 0)\\ f_{i-1,-1} + 2 &(a_i = 1) \end{cases}\\ f_{i,0} &= \begin{cases} \min(f_{i-1,-1}, f_{i-1,0}) &(a_{i}=0)\\ f_{i-1,-1} + 1 &(a_{i} = 1) \end{cases}\\ f_{i,1} &= \begin{cases} f_{i-1,1} + 2 &(a_i = -1)\\ f_{i-1,1} + 1 &(a_i = 0)\\ \min(f_{i-1,-1},f_{i-1,0},f_{i-1,1}) &(a_i = 1) \end{cases} \end{aligned}\]

\[\]

答案即 \(\min(f_{n,-1},f_{n,0},f_{n,1})\)。

总时间复杂度 \(O(n)\)。


爆零小技巧

当然不可能真的用负数当下标啦!


代码实现

//知识点:线性 DP 
/*
By:Luckyblock
*/
#include <algorithm>
#include <cctype>
#include <cstdio>
#include <cstring>
#define LL long long
const int kN = 1e6 + 10;
//=============================================================
int n, ans, a[kN], f[kN][3];
//=============================================================
inline int read() {
  int f = 1, w = 0;
  char ch = getchar();
  for (; !isdigit(ch); ch = getchar())
    if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) {
    w = (w << 3) + (w << 1) + (ch ^ '0');
  }
  return f * w;
}
void Chkmax(int &fir_, int sec_) {
  if (sec_ > fir_) fir_ = sec_;
}
void Chkmin(int &fir_, int sec_) {
  if (sec_ < fir_) fir_ = sec_;
}
//=============================================================
int main() {
  n = read();
  int fir1 = 0, firm1 = 0;
  for (int i = 1; i <= n; ++ i) {
    a[i] = read();
    if (!fir1 && a[i] == 1) fir1 = i;
    if (!firm1 && a[i] == -1) firm1 = i;
  }
  if (1 < firm1 && firm1 < fir1) {
    printf("BRAK\n");
    return 0; 
  }
  
  memset(f, 63, sizeof (f));
  f[1][a[1] + 1] = 0;
  for (int i = 2; i <= n; ++ i) {
    f[i][0] = f[i - 1][0] + a[i] + 1;
    
    if (a[i] == 0) {
      f[i][1] = std::min(f[i - 1][0], f[i - 1][1]);
    } else if (a[i] == 1) {
      f[i][1] = f[i - 1][0] + 1;
    }
    
    if (a[i] != 1) {
      f[i][2] = f[i - 1][2] + 1 - a[i];
    } else {
      Chkmin(f[i][2], f[i - 1][0]);
      Chkmin(f[i][2], f[i - 1][1]);
      Chkmin(f[i][2], f[i - 1][2]);
    }
  }
  
  ans = f[n + 1][0];
  Chkmin(ans, f[n][0]);
  Chkmin(ans, f[n][1]);
  Chkmin(ans, f[n][2]);
  if (ans == f[n + 1][0]) {
    printf("BRAK\n");
  } else {
    printf("%d\n", ans);  
  }
  return 0;
}

标签:fir,ch,int,POI2013,cdots,le,cases,BAJ,P3558
来源: https://www.cnblogs.com/luckyblock/p/13962576.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有