线性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;
}
原文地址:https://www.cnblogs.com/EricQian/p/15366381.html