• 线性DP


    动态规划简介:动态规划是在一个困难的 嵌套决策链 中,决策出最优解。动态规划有 可推导性 ,但同时,动态规划也有 无后效性 ,即 每个当前状态会且仅会决策出下一状态,而不直接对未来的所有状态负责

    子序列问题

    首先声明2个名词:

    $ operatorname{LIS} $ :Longest Increasing Subsequence 最长递增子序列

    $ operatorname{LCS} $ :Longest Common Subsequence 最长公共子序列

    我们知道,用朴素的 $ operatorname{DP} $ 解 $ operatorname{LIS} $ 和 $ operatorname{LCS} $ ,复杂度都是 (O(n^2)) 的,而且很容易写出来。

    for(int i=1;i<=n;i++)
    {
    	 dp[i]=1;
    	 for(int j=1;j<i;j++)
    	 if(val[j]<val[i] && dp[i]<dp[j]+1) dp[i]=dp[j]+1,pre[i]=j;
    } // LIS
    
    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(a[i]==b[j]) dp[i][j]=max(dp[i][j],dp[i-1][j-1]+1);
    	 }
    } // LAS
    

    ↑ 这便是极为低效的 (O(n^2)) 做法

    考虑用 (O(n log n)) 算法:用 (dp[i]) 表示在该序列中,上升子序列长度为 (i) 的上升子序列,的最小末尾数值。这样可以二分求出比当前数值小的最长子序列。

    $ operatorname{LIS} $ 核心代码:

    memset(dp,inf,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++)
    {
    	 if(val[i]>dp[ans]) dp[++ans]=val[i]; // 比之前最长的子序列的最大值还大,则 ans += 1 
    	 else
    	 {
    	 	 int l=0,r=ans,ret=0;
    	 	 while(l<r)
    	 	 {
    	 	 	 int mid=(l+r)/2;
    	 	 	 if(dp[mid]<=val[i]) l=mid+1; // 找到第一个比 dp[...] 小的数 (这一题中是严格递增)
    	 	 	 else r=mid-1,ret=mid;
    		 }
    		 dp[l]=min(dp[l],val[i]);
    	 }
    }
    

    在特殊条件下,即两个序列的元素种类相同,可以用类似离散化的方式将一个序列变为 (1…n) 的序列,此时的 $ operatorname{LCS} $ 就是另一个序列的 $ operatorname{LIS} $ 则在这种条件下可以 (O(nlogn)) 地处理 $ operatorname{LCS} $ 问题

    $ operatorname{LCS} $ 核心代码:

    memset(dp,inf,sizeof(dp));
    dp[0]=0;
    for(int i=1;i<=n;i++)
    {
    	 if(Turn[b[i]]>dp[ans]) dp[++ans]=Turn[b[i]]; // 比之前最长的子序列的最大值还大,则 ans += 1 
    	 else
    	 {
    	 	 int l=0,r=ans,ret=0;
    	 	 while(l<r)
    	 	 {
    	 	 	 int mid=(l+r)/2;
    	 	 	 if(dp[mid]<=Turn[b[i]]) l=mid+1; // 找到第一个比 Turn[b[i]] 小的数 
    	 	 	 else r=mid-1,ret=mid;
    		 }
    		 dp[l]=min(dp[l],Turn[b[i]]);
    	 }
    }
    

    LIS变形——平面吃豆子问题

    题意:给定一张平面直角坐标系,你现在处在 ((0,0)) 的位置,而有些网格点上有豆子。如果你在 ((x,y)) ,只能前往 ((x+1,y),(x,y+1),(x+1,y+1)) 。请问最多能够吃到多少颗豆子?

    (nle 10^5,-10^9le x_i,y_i le 10^9)

    首先找出 (x_ige 0,y_ige 0) 的所有点,并对他们根据 (x) 为第一关键字,(y) 为第二关键字排序。

    之后就对所有 (y) 跑一边 (operatorname{LIS}) 就可以啦

    $ exttt{code}$
    #define inf 0x3f3f3f3f
    #define Maxn 100005
    int n,m,ans,dp[Maxn];
    struct Data { int x,y; }a[Maxn];
    bool cmp(Data x,Data y)
    {
    	 if(x.x!=y.x) return x.x<y.x;
    	 return x.y<y.y;
    }
    int main()
    {
    	 n=rd();
    	 for(int i=1,x,y;i<=n;i++)
    	 {
    	 	 x=rd(),y=rd();
    	 	 if(x<0 || y<0) { i--,n--; continue; }
    	 	 a[i]=(Data){x,y};
    	 }
    	 sort(a+1,a+n+1,cmp);
    	 memset(dp,inf,sizeof(dp)),dp[0]=0;
    	 for(int i=1;i<=n;i++)
    	 {
    	 	 if(a[i].y>=dp[ans]) dp[++ans]=a[i].y;
    	 	 else
    	 	 {
    	 	 	 int nl=0,nr=ans,mid,ret=0;
    	 	 	 while(nl<=nr)
    	 	 	 {
    	 	 	 	 mid=(nl+nr)>>1;
    	 	 	 	 if(dp[mid]<=a[i].y) nl=mid+1;
    	 	 	 	 else nr=mid-1,ret=mid;
    			 }
    			 dp[ret]=min(dp[ret],a[i].y);
    		 }
    	 }
    	 printf("%d
    ",ans);
         return 0;
    }
    
  • 相关阅读:
    JAVA向上转型和向下转型
    LeetCode记录之9——Palindrome Number
    LeetCode记录之7——Reverse Integer
    JAVA数据结构--插入排序
    JAVA数据结构--选择排序
    JAVA数据结构--冒泡排序
    HTTP协议04-返回状态码
    HTTP协议03-http特点及请求方式
    HTTP协议02-请求和响应的报文构成
    HTTP笔记01-http相关的基础知识
  • 原文地址:https://www.cnblogs.com/EricQian/p/15366381.html
Copyright © 2020-2023  润新知