#背包#nssl 1488 上升子序列
题目
给一个长度为\(n\)的数组\(a\)。试将其划分为两个严格上升子序列,并使其长度差最小。
分析
当\(max([1,i])<min([i+1,n])\)时显然两个区间互不影响,把\(i\)视为分界点
若相邻的两个分界点\(i,j\)所组成的区间\([i\sim j]\)如果合法只有一种划分方法
所以把合法区间贡献扔入背包里搞一搞就可以了
代码
#include <cstdio>
#include <cctype>
#include <bitset>
#define rr register
using namespace std;
const int N=100011; bitset<N>dp;
int n,mx[N],mn[N],a[N],flag,ans,las;
inline signed iut(){
rr int ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(int ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline bool check(int l,int r){
rr int fi=0,se=0,TOT=0;
for (rr int i=l;i<=r;++i)
if (fi<a[i]) fi=a[i],++TOT;
else if (se<a[i]) se=a[i];
else return 0;
dp=(dp<<TOT)|(dp<<(r-l+1-TOT));
return 1;
}
signed main(){
for (rr int Test=iut();Test;--Test){
n=iut(),mx[0]=-1,mn[n+1]=0x3f3f3f3f,
flag=0,dp.reset(),dp[0]=las=1,ans=-1;
if (n==1) printf("-1\n");
for (rr int i=1;i<=n;++i) a[i]=iut();
for (rr int i=1;i<=n;++i) mx[i]=mx[i-1]>a[i]?mx[i-1]:a[i];
for (rr int i=n;i>=1;--i) mn[i]=mn[i+1]<a[i]?mn[i+1]:a[i];
for (rr int i=1;i<=n;++i)
if (mx[i]<mn[i+1]){
if (!check(las,i)){
flag=1;
break;
}
las=i+1;
}
if (!flag){
for (rr int i=n/2;~i;--i)
if (dp[i]){
ans=n-i*2;
break;
}
}
printf("%d\n",ans);
}
return 0;
}