• 【CF568E】Longest Increasing Subsequence(动态规划)


    点此看题面

    • 给定一个长度为(n)的序列,其中有(k)个空缺。
    • 你有(m)个数可以用于填补空缺(不能重复使用)。
    • 要求最大化最长上升子序列长度,给出一个方案。
    • (n,mle10^5,kle10^3)

    最长上升子序列的套路

    考虑最长上升子序列的常见(DP)套路,即设(f_i,g_i)分别表示使得最长上升子序列长度为(i)的最小值及其位置。

    然后对于已知的位置,它的值是固定的,我们再额外记(p_i,lst_i)表示以(i)为结尾的最长上升子序列长度以及转移的前一个位置。

    转移的时候,已知位置显然可以直接二分转移点转移。

    至于未知位置,个数(kle10^3),由于转移点随着填入值的单调变化也是单调移动的,我们可以直接双指针维护转移点转移。

    但这道题的难点应该在于方案,而方案的难点在于在最长上升子序列中的未知位置(不在最长上升子序列中的未知位置可以任意填入未填过的数)。

    对于一个未知位置,如果能在它前面找到一个长度比它恰少(1)且值比它小的已知位置,那么我们可以认为它是从这个位置转移过来的。

    否则,说明它必然是从一个未知位置转移过来的,那么不妨认为它就是从前一个未知位置转移的,显然不会使答案变劣。

    代码:(O(nlogn+mk))

    #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
    #define K 1000
    using namespace std;
    int n,m,a[N+5],b[N+5],c,s[N+5],p[N+5],lst[N+5];
    struct S
    {
    	int x,y;I S(CI a=0,CI b=0):x(a),y(b){}
    	I bool operator >= (Con S& o) Con {return x>=o.x;} 
    	I bool operator < (Con S& o) Con {return x<o.x;} 
    }f[N+5];
    int v[N+5];I void Fill(CI x,CI y)//求方案
    {
    	#define G(x) (lower_bound(b+1,b+m+1,x)-b-1)
    	if(y==1) return;RI k=lst[x];if(!k)
    	{
    		for(k=1;k^x;++k) if(~a[k]&&p[k]==y-1&&a[k]<a[x]) break;if(k==x) W(~a[--k]);//未知位置寻找其转移位置
    	}
    	return !~a[k]&&(a[k]=b[G(a[x])],v[G(a[x])]=1),Fill(k,y-1);//如果前一个位置未知,则给它一个尽可能大的值
    }
    int main()
    {
    	RI i,x,y,w,t=0;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d",a+i);a[++n]=2e9;//在最后加一个极大值
    	for(scanf("%d",&m),i=1;i<=m;++i) scanf("%d",b+i);
    	for(sort(b+1,b+m+1),i=1;i<=m;++i) s[i]=b[i];c=unique(s+1,s+m+1)-s-1;//排序去重
    	for(i=1;i<=n;++i) if(~a[i])
    		f[p[i]=f[t]<a[i]?++t:lower_bound(f+1,f+t+1,a[i])-f]=S(a[i],i),lst[i]=f[p[i]-1].y;//已知位置直接二分转移
    	else for(f[t]<s[c]&&(f[++t]=S(s[c],i),0),x=c,y=t;x;f[y]=S(s[x--],i)) W(y^1&&f[y-1]>=s[x]) --y;//双指针维护转移点
    	for(Fill(f[t].y,t),i=x=1;i^n;printf("%d ",a[i++])) if(!~a[i]) {W(v[x]) ++x;a[i]=b[x++];}return 0;//输出方案
    }
    
  • 相关阅读:
    QT学习——dialog、widget、mainwindow的区别和选择
    剑指offer——二叉树的深度
    位运算实现加减乘除四则运算
    剑指offer——求两个整数和
    C++常用设计模式
    从编程实现角度学习 Faster R-CNN(附极简实现)
    剑指offer——最小的k个数
    剑指offer——对称二叉树
    java 定时器
    rocketmq consumer接收到的MessageExt中各个字段的说明
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF568E.html
Copyright © 2020-2023  润新知