文章目录

  • P1020 [NOIP1999 普及组] 导弹拦截
  • P1439 【模板】最长公共子序列
  • P1637 三元上升子序列
  • 272. 最长公共上升子序列
  • LCIS
  • 273. 分级
  • Sequence
  • P4597 序列 sequence

P1020 [NOIP1999 普及组] 导弹拦截

P1020 [NOIP1999 普及组] 导弹拦截

导弹拦截应该是接触DP的第一题(只不过洛谷上的数据加强了,按照上图的 O ( N 2 ) O(N^2) O(N2)做法过不去QAQ可以改用二分。

Dilworth定理:偏序集能划分成的最少的全序集个数等于最大反链的元素个数

把一个数列划分成最少的最长不升子序列的数目就等于这个数列的最长上升子序列的长度(LIS)

#include<bits/stdc++.h>
using namespace std;
int a[100010],LDS[100010],LIS[100010];
int main()
{int x,i=0,len=1;while(~scanf("%d",&x))a[i++]=x;LDS[0]=a[0];for(int j=1;j<i;j++){if(a[j]<=LDS[len-1])LDS[len++]=a[j];else{int l=0,r=len;while(l<r){int mid=(l+r)>>1;if(a[j]>LDS[mid]) r=mid;else l=mid+1;}LDS[l]=a[j];}}cout<<len<<endl;LIS[0]=a[0];int len2=1;for(int j=1;j<i;j++){if(a[j]>LIS[len2-1])LIS[len2++]=a[j];else{int l=0,r=len2;while(l<r){int mid=(l+r)>>1;if(a[j]<=LIS[mid]) r=mid;else l=mid+1;}LIS[l]=a[j];}}cout<<len2<<endl;return 0;
}

P1439 【模板】最长公共子序列

P1439 【模板】最长公共子序列

也不给你用上图的朴素DP过去( O ( N 2 ) O(N^2) O(N2))www

for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){dp[i][j]=max(dp[i-1][j],dp[i][j-1]);if(a1[i]==a2[j])dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);}cout<<dp[n][m];

但本题数据有个明显的特征即两个序列都是全排列,也就是说我们可以重新构造一种映射关系。

#include<bits/stdc++.h>
using namespace std;
int a[100010],b[100010],LIS[100010];
int main()
{int n,i;cin>>n;//由于a b 中元素完全相同,只是顺序不同,可以利用映射关系,将a映射成一个单调递增序列 for(int i=1;i<=n;i++){int x;scanf("%d",&x);a[x]=i;}//相应地,由a给出的映射关系,可将b构造成一个新的序列 for(int i=1;i<=n;i++){int y;scanf("%d",&y);b[i]=a[y];}// 两个序列的子序列,一定是a的子序列。而a本身就是单调递增的。因此这个子序列是单调递增的。//换句话说,只要这个子序列在b中单调递增,它就是a的子序列//哪个最长呢?当然是b的LIS最长。下用二分+贪心求b的LIS即可 LIS[1]=b[1];int len2=1;for(int j=2;j<=n;j++){if(b[j]>LIS[len2])LIS[++len2]=b[j];//如果此时检索到的数比LIS的最后一个数(也即最大的数)大,直接插到LIS尾部 else//否则,在LIS前面的数中二分查找合适的位置进行替换 {int l=1,r=len2;while(l<r){int mid=(l+r)>>1;if(b[j]<=LIS[mid]) r=mid;else l=mid+1;}LIS[l]=b[j];}}cout<<len2<<endl;return 0;
}

P1637 三元上升子序列


P1637 三元上升子序列
UVA12983 The Battle of Chibi

给定一个长度为n的序列,求其包含的m元单调上升子序列个数。
见求LIS思DP。导弹拦截那种题,开f[结尾编号]=序列长度,信息量不够,考虑定义f[序列长度][结尾编号] =序列个数。

状态转移: f [ i ] [ j ] = ∑ f [ i − 1 ] [ k ] , 1 < = k < j , a [ k ] < a [ i ] f[i][j]=\sum f[i-1][k],1<=k<j,a[k]<a[i] f[i][j]=∑f[i−1][k],1<=k<j,a[k]<a[i]

这样做的时间复杂度为 O ( n 2 ∗ m ) O(n^2*m) O(n2∗m)

得想办法在更短时间内统计出 ∑ f [ i − 1 ] [ k ] \sum f[i-1][k] ∑f[i−1][k],树状数组或者线段树都可以实现在 O ( l o g n ) O(log n) O(logn)内的区间统计。

先离散化,将a[]的分布集中起来,以a[k]为下标储存f[i-1][k]的值。

此时针对状态转移方程写两层循环:外层循环枚举长度,每次枚举到一个长度都重置一下树状数组c[]。{内层枚举序列结尾坐标,此前已将f[i-1][k]增加到树状数组中,此时只需用树状数求和即可。然后把f[i-1][j]加到树状数组中,这个值只会更新到比a[j]大的数(对应状态转移方程里的a[k]<a[i]),这些数后续会被调用(对应状态转移方程里的1<=k<j)}。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=30010;
ll n,f[4][N],a[N],b[N],c[N],ans;ll lowbit(ll x) { return x&(-x);}void update(ll p,ll v)
{for(int i=p;i<=n;i+=lowbit(i))c[i]+=v;
} ll ask(int p)
{ll sum=0;for(int i=p;i;i-=lowbit(i))sum+=c[i];return sum;
}int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%lld",&a[i]),b[i]=a[i];sort(b+1,b+n+1);unique(b+1,b+n+1)-(b+1);for(int i=1;i<=n;i++){a[i]=lower_bound(b+1,b+n+1,a[i])-b;//二分查找在数组中的位置f[1][i]=1;//初始化:长度唯一,本身为结尾}//离散化结束,初始化结束,准备工作完成for(int i=2;i<=3;i++)//枚举长度{memset(c,0,sizeof(c));//重置树状数组for(int j=1;j<=n;j++){f[i][j]=ask(a[j]-1);//求和update(a[j],f[i-1][j]);//更新}}for(int i=1;i<=n;i++)ans+=f[3][i];cout<<ans; return 0;
}

272. 最长公共上升子序列

272. 最长公共上升子序列

是LIS和LCS的结合体,可以尝试用a解决公共子序列问题,用b解决上升子序列问题。
定义 f [ i ] [ j ] f[i][j] f[i][j]: a [ 1 ] − a [ i ] , b [ 1 ] − b [ j ] a[1]-a[i],b[1]-b[j] a[1]−a[i],b[1]−b[j]可以构成的以 b [ j ] b[j] b[j]结尾的LCIS的长度。

if(A[i]==B[j]) F[i,j]=F[i-1,j];
if(A[i]!=B[j],F[i,j]=max{F[i-1,k]}+1;(0<=k<j)

未优化前,
考虑当a[i]==b[j]时,上一步从b[1]~b[j]中的某一个b[k]转化而来。

#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int f[N][N];
int n,a[N],b[N];
int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++)for(int j=1;j<=n;j++){f[i][j]=f[i-1][j];if(a[i]==b[j]){int maxv=1;for (int k=1;k<j;k++)if (a[i]>b[k]) maxv=max(maxv, f[i - 1][k] + 1);f[i][j]=max(f[i][j], maxv);}}int ans=0;for(int j=1;j<=n;j++)ans=max(ans,f[n][j]);cout<<ans<<endl;return 0;
}

TLE了,发现可以没必要枚举k,可以用val实时更新前缀的max长度。

#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int f[N][N];//a1~ai,b1~bj可以构成的以bj结尾的LCIS的长度
int n,a[N],b[N],ans=0,m;
int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++){int val=0;//(i,0)的LCIS长度//i固定,b1->bn时LCIS长度一定是单调递增的for(int j=1;j<=n;j++){if(a[i]==b[j]) f[i][j]=val+1;//遍历到一个ai、bj相同的位置,长度++else f[i][j]=f[i-1][j];//不相等,直接继承(i-1,j)的长度if(b[j]<a[i]) val=max(val,f[i-1][j]);//更新val:(i,j)前面的LCIS长度//另一种情况:(b[j]>a[i])一定不会影响到LCIS的长度}}for(int j=1;j<=n;j++)//找出所有以bj结尾的LCIS中长度最大的ans=max(ans,f[n][j]);cout<<ans<<endl;return 0;
}

进一步发现f的第一维也是可以省略的。

#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int f[N];//a1~ai,b1~bj可以构成的以bj结尾的LCIS的长度
int n,a[N],b[N],ans=0,m;
int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%d",&a[i]);for(int i=1;i<=n;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++){int val=0;//(i,0)的LCIS长度//i固定,b1->bn时LCIS长度一定是单调递增的for(int j=1;j<=n;j++){if(a[i]==b[j]) f[j]=val+1;//遍历到一个ai、bj相同的位置,长度++if(b[j]<a[i]) val=max(val,f[j]);//更新val:(i,j)前面的LCIS长度//另一种情况:(b[j]>a[i])一定不会影响到LCIS的长度}}for(int j=1;j<=n;j++)//找出所有以bj结尾的LCIS中长度最大的ans=max(ans,f[j]);cout<<ans<<endl;return 0;
}

LCIS

LCIS

加上路径输出,scheme保存前一个位置。

#include<bits/stdc++.h>
using namespace std;
const int N=3010;
int f[N];//a1~ai,b1~bj可以构成的以bj结尾的LCIS的长度
int n,a[N],b[N],ans[N],m,scheme[N],p;
int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%d",&a[i]);cin>>m;for(int i=1;i<=m;i++)scanf("%d",&b[i]);for(int i=1;i<=n;i++){int id=0;//i固定,b1->bn时LCIS长度一定是单调递增的for(int j=1;j<=m;j++){if(a[i]==b[j]) f[j]=f[id]+1,scheme[j]=id;//遍历到一个ai、bj相同的位置,长度++,记录下j前面一个位置是id if(b[j]<a[i]&&f[id]<f[j]) id=j;}}int p=0;for(int i=1;i<=m;i++)if(f[i]>f[p]) p=i;//找出所有以bj结尾的LCIS中长度最大的printf("%d\n",f[p]);int top=0;while(f[p]--) {ans[++top]=b[p];id=scheme[p];//从后往前倒出来 }for(int i=top;i;i--)printf("%d ",ans[i]);//输出方案前需要记录return 0;
}

273. 分级

273. 分级

即将一个序列转化成非严格单调递增(或者递减)序列的最小代价。
引理:在满足ans最小的情况下,一定存在一种构造序列b的方法,使得b中的数值都在a中出现过。

状态转移:F[i,j]=min{F[i-1,k]}+|A[i]-j|;(0<=k<j)

像LCIS那题所述,优化枚举k的一维。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=2020;
LL a[N],b[N],f[N][N];
//f[i][j]表示已经考虑前a[i]个且最后一个以b[j]结尾的最优解
LL ans=1e18;
int n;void work()
{for(int i=1;i<=n;i++){LL temp=1e18;for(int j=1;j<=n;j++){temp=min(temp,f[i-1][j]);//对前缀取最小f[i][j]=temp+abs(a[i]-b[j]);}}for(int j=1;j<=n;j++)ans=min(ans,f[n][j]);
}
int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];//引理:在满足ans最小的情况下,一定存在一种构造序列b的方法,使得b中的数值都在a中出现过sort(b+1,b+n+1);work();//b[i]单调递增的情况下取一遍答案reverse(b+1,b+n+1);work();//b[i]单调递减的情况下取一遍答案cout<<ans<<endl;return 0;

Sequence

Sequence

比起上一题会卡空间,要优化一维。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N=5050;
LL a[N],b[N],f[N];
int n;int main()
{cin>>n;for(int i=1;i<=n;i++)scanf("%lld",&a[i]),b[i]=a[i];//引理:在满足ans最小的情况下,一定存在一种构造序列b的方法,使得b中的数值都在a中出现过sort(b+1,b+n+1);for(int i=1;i<=n;i++){for(int j=1;j<=n;j++){f[j]+=abs(b[j]-a[i]);if(j>1)   f[j]=min(f[j],f[j-1]);}}cout<<f[n];return 0;
}

P4597 序列 sequence

P4597 序列 sequence

卡 O ( N 2 ) O(N^2) O(N2)的时间复杂度啦,要用堆优化。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n,ans,x;
priority_queue<LL>q;
int main()
{cin>>n;for(int i=1;i<=n;i++){scanf("%lld",&x);q.push(x);if(q.top()>x){ans+=q.top()-x;q.pop();q.push(x);}}cout<<ans;return 0;
}

P.S.
Q:把一个序列变成非严格单调递增的,至少需要修改几个数? A:序列A的总长度减去A的最长不下降子序列长度。
Q:把一个序列A变成严格单调递增的,至少需要修改几个数? A:构造序列 B [ i ] = A [ i ] − i B[i]=A[i]-i B[i]=A[i]−i,答案为序列总长度减去B的最长不下降子序列长度。

LIS.LCS.LCIS相关问题相关推荐

  1. LCS、LIS及LCIS

    最长公共子序列(Longest Common Subsequence) 即要求两数列的最长的公共的子序列(可以间断,区别"子串") 考虑f[i][j]f[i][j]f[i][j]表 ...

  2. JoyOI(TYVJ)1071-LCIS【线性dp,LIS,LCS】

    正题 题目链接:http://www.joyoi.cn/problem/tyvj-1071 题目大意 求两个序列的最长公共上升子序列. code 我们先回顾一下LIS和LCS LIS: fi=max{ ...

  3. B - LIS LCS

    题意介绍 给两个序列,求第一个序列的LIS长度和两个序列的LCS长度 题意分析 最长上升子序列 状态:定义 fi 表示以 Ai 为结尾的最长上升序列的方程. 初始化:f1 = 1 转移过程 输出答案: ...

  4. Week10 作业B - LIS LCS

    目录 1.题意 2.样例 3.解题思路 4.AC代码 1.题意 东东有两个序列A和B. 他想要知道序列A的LIS和序列AB的LCS的长度. 注意,LIS为严格递增的,即a1<a2<-< ...

  5. LCIS vijos—P1264

    今天我们来看一道关于dp的经典例题 是关于LCIS的 先看一下题干 身为拜月教的高级间谍,你的任务总是逼迫你出生入死.比如这一次,拜月教主就派你跟踪赵灵儿一行,潜入试炼窟底. 据说试炼窟底藏着五行法术 ...

  6. LIS的三种求解方法

    1.  O(n^2) 传统的求解方法 ,思路为dp,状态转移方程为 dp[i]=max( dp[j]+1,1) 即到目前的i为止,对前面出现的a[j](j<i)进行遍历 ,如果出现了a[i]&g ...

  7. 过去可忆,未来可期(随心录+杂记)

    2019.5.30 去年的这个时候心智FAMILY已经在前往苏州的火车上愉快狂欢颓废了. 历史总是在重演,只是已经今非昔比,物是人非. 明天的机房就要空荡许多了啊,长达30天不能再和我的风浔凌大佬一起 ...

  8. 程序员面试题精选100题(20)-最长公共子串[算法]

    题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串.注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中.请编写一个函数,输入两个字符串, ...

  9. 程序员面试100题之六:最长公共子序列

           题目:如果字符串一的所有字符按其在字符串中的顺序出现在另外一个字符串二中,则字符串一称之为字符串二的子串.注意,并不要求子串(字符串一)的字符必须连续出现在字符串二中.请编写一个函数,输 ...

最新文章

  1. 如何从Subversion存储库中git-svn克隆最后n个修订版?
  2. mybatis_基础篇
  3. QQ协议分析及其还原(二)
  4. JQuery-学习笔记04【基础——JQuery基础案例】
  5. 阿里云朱照远:视频云2.0,更大规模、更智能、更清晰
  6. NIFI如何利用eclipse开发自己的Processor(下)
  7. 教你搭建一个NAT实验环境
  8. mysql5.6.19安装图解_mysql5.6.19安装说明
  9. MySQL中查询字段为空或者为null方法
  10. Internet Tv Radio Player v5.2
  11. MySQL复制表的三种方式
  12. 网页游戏对java的技术要求_网页制作谈谈什么技术是Java开发网页游戏的必要条件呢?怎样在微信公众平台上制作5级游戏?...
  13. 数据挖掘实践 —— OneR 分类算法
  14. 使用开源激光SLAM方案LIO-SAM运行KITTI数据集,如有用,请评论雷锋
  15. 立交匝道中边桩坐标放样正反算程序RAMP
  16. 在本地电脑将ip与名字相关联
  17. logit回归模型_你们要的二项Logit模型在这里——离散选择模型之八
  18. Android 后台开发
  19. OpenWrt下使用docker安装icloudpd实现iPhone照片备份私有云盘nas
  20. cocos2d-x基本面试题

热门文章

  1. vbscript 学习笔记
  2. 信号间隔是什么意思_地铁信号里的行车闭塞是啥意思?
  3. CSS 的权重和优先级
  4. 你面试遇到过哪些奇葩公司
  5. 铁路行李随车托运办理
  6. 分享小程序会员积分管理系统的优势_会员积分管理系统的作用
  7. C++ 牛客网做题总结
  8. Java自学笔记Day22
  9. 探秘Sophos反病毒实验室监测主流病毒全过程
  10. 【小5聊】C# Json字符串转Model实体类以及Model实体类转Json字符串