博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
计蒜之道 百度AI小课堂-上升子序列
阅读量:6544 次
发布时间:2019-06-24

本文共 2525 字,大约阅读时间需要 8 分钟。

题目描述

给一个长度为 \(n\) 的数组 \(a\) 。试将其划分为两个严格上升子序列,并使其长度差最小。

输入格式

输入包含多组数据。

数据的第一行为一个正整数 \(T\) ,表示数据组数。

每组数据包括两行:

第一行包括一个正整数 \(n\)

第二行包括一个长度为 \(n\) 的数组 \(a\)

输出格式

对于每组数据输出一行一个整数,表示两个子序列的最小长度差。若不存在划分方案则输出\(-1\)

数据范围

\(T <= 10\)

简单: \(n <= 10^5, 0 <= a_i <= 2^{30}\), 最多只存在一种合法的划分方案
中等: \(n <= 10^3, 0 <= a_i <= 2^{30}\), 划分方案不超过\(10^{18}\)
困难: \(n <= 10^5, 0 <= a_i <= 2^{30}\), 划分方案不超过\(10^{18}\)

思路:直接上题解

如果一个位置\(k\)满足对于\(∀_i∈[1,k], ∀_j∈[k+1,n], a_i < a_j\)恒成立,那么称\(k\)为一个分界点。找出所有分界点后可将序列分为若干段,每段之间相互独立,同时每段只存在不超过一种合法的方案数。求出每段的划分\(O(n)\),再进行背包dp(\(O(nlogK)\)(\(K\)为可能的划分方案数))

其实就是说对于某个\(i\),满足\(max(1,i) < min(i + 1,n)\),则序列\([1,i]和[i+1,n]\)是相互独立的。

同理再来一个\(j > i\) 满足\(max(1,j) < min(j + 1,n)\), 则序列\([1,j]和[j+1,n]\)相互独立, 序列\([1,i]\)已经被划分出去,所以左边剩下\([i+1,j]\)
简单证明这样的每段如果合法,只存在一种划分。
即证明对于合法的一段\([i,j](j > i + 1)\), 若不存在\(k\),满足\(i<=k<j, max(i,k) < min(k+1,j)\),那么段\([i,j]\)只有一种划分方案。
反证不存在的情况有多种划分成立, 则存在的情况下只有一种划分,存在的情况下可以分成不可再分的几段,每段有多种划分,拼起来也会有多种划分, 与只有一种划分矛盾。
每段只有一种划分, \(m\)段组合起来的方案数为\(2^m = K\), 所以\(m = logK\)

最后问题变成一个背包,从每段中选择一个序列长度组合起来,最后是否出现长度为\(j\)的序列(\(dp[j]\))

#include
#define LL long long using namespace std;const int INF = 1e9;const int N = 1e5 + 10;int a[N];int mi[N], mx[N];int dp[2][N];int T, n, o;bool gao(int l, int r){ int first = 0, second = 0, cnt = 0; for(int i = l;i <= r;i++){ if(first < a[i]) first = a[i], cnt++; else if(second < a[i]) second = a[i]; else return false; } int d[2] = {cnt, r - l + 1 - cnt}; for(int i = 0;i <= n;i++) { dp[o ^ 1][i] = 0; for(int k = 0;k < 2;k++){ if(i >= d[k]) dp[o ^ 1][i] |= dp[o][i - d[k]]; } } o ^= 1; return true;}int main(){ cin>>T; while(T--){ cin>>n; for(int i = 1;i <= n;i++) cin>>a[i]; if(n == 1){ cout<<-1<
<= n;i++) mx[i] = max(mx[i - 1], a[i]); for(int i = n;i >= 1;i--) mi[i] = min(mi[i + 1], a[i]); memset(dp, 0, sizeof(dp)); dp[0][0] = 1; int last = 1; bool flag = true; o = 0; for(int i = 1;i <= n;i++){ if(mx[i] < mi[i + 1]){ if(!gao(last, i)){ flag = false; break; } last = i + 1; } } int ans = -1; if(flag){ for(int i = n / 2;i >= 0;i--) if(dp[o][i]){ ans = n - 2 * i; break; } } cout<
<

转载于:https://www.cnblogs.com/jiachinzhao/p/10939332.html

你可能感兴趣的文章
母线的种类与作用是什么(转)
查看>>
【Xamarin 挖墙脚系列:IOS 开发界面的3种方式】
查看>>
Atitit.工作流系统的本质是dsl 图形化的dsl 4gl
查看>>
4-5-创建索引表-串-第4章-《数据结构》课本源码-严蔚敏吴伟民版
查看>>
go run main.go undefined? golang main包那点事
查看>>
从零开始写一个npm包,一键生成react组件(偷懒==提高效率)
查看>>
中国最强的人工智能学术会议来了
查看>>
Metasploit的射频收发器功能 | Metasploit’s RF Transceiver Capabilities
查看>>
主库 归档 删除策略
查看>>
《Linux从入门到精通(第2版)》——导读
查看>>
路过下载攻击利用旧版 Android 漏洞安装勒索软件
查看>>
ThinkSNS 六大子版本体验及源码下载
查看>>
《算法基础》——1.5实际因素
查看>>
《Java数字图像处理:编程技巧与应用实践》——第3章 基本Swing UI组件与图像显示 3.1 JPanel组件与BufferedImage对象的显示...
查看>>
为什么有人讨厌 Google 的新 Logo?
查看>>
腾讯2017暑期实习编程题3
查看>>
Intellij IDEA 构建Spring Web项目 — 用户登录功能
查看>>
[AHOI2013]作业
查看>>
git push被忽略的文件 处理
查看>>
C#中用ILMerge将所有引用的DLL打成一个DLL文件
查看>>