【CF1479B2】Painting the Array II(简单DP)

点此看题面

  • 给定一个长度为(n)的数组,每个位置上有一个颜色。
  • 你需要把它划分成两个子序列,求这两个子序列中颜色段数之和的最小值。
  • (nle10^5)

动态规划

考虑分配完第(i)个元素之后,两个子序列中一个末尾肯定是(a_i),而另一个末尾我们不妨假设为(j)

于是设(f_{i,j})表示分配完第(i)个元素,另一个子序列末尾是(j)时,之前颜色段数之和的最小值。

转移分两种:

  • (a_i)放在(a_{i-1})后面,则(f_{i,j}=f_{i-1,j}+[a_{i} ot=a_{i-1}])
  • (a_i)放在(j)后面,则(f_{i,a_{i-1}}=f_{i-1,j}+[a_i ot=j])

于是这样就能得到一个(O(n^2))的暴力(DP)

简单的优化

如果数据结构学傻的话看到这种式子可能直接就上线段树了。。。(当然也不是不可以)

然而我们发现,第一种转移实际上是全局修改,第二种转移其实可以拆成全局询问最小值和对(f_{i-1,a_i})的单点询问、对(f_{i,a_{i-1}})的单点修改。

全局修改直接记录一个全局(tag)就可以了。由于除去(tag)之后所有元素都只会越来越小,因此全局询问只需要维护一个全局最小值

代码:(O(n))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
using namespace std;
int n,a[N+5],f[N+5];
int main()
{
      RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i),f[i]=1e9;//初始化
      RI t,tg=0,Mn=0;for(i=1;i<=n;++i) t=min(Mn+tg+1,f[a[i]]+tg),//t记录第二种转移值
            a[i-1]^a[i]&&++tg,f[a[i-1]]+tg>t&&(Mn=min(Mn,f[a[i-1]]=t-tg));//tg记录第一种转移的全局标记,Mn记录全局最小值
      return printf("%d
",Mn+tg),0;//答案就是最小值+全局标记
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF1479B2.html