dp_基础_最长上升子序列
也是非常经典的一道题
比较简单的做法是:
设dp[i] i: 以nums[i]作为一个子数组的,最长的上升子序列
则有
无后效性:i肯定不会影响i之前的。
子问题重叠:算i+1,i+2 .. n 时,都需要dp[i]
最优子结构:for( int j=0;j<i;++j ) if ( nums[j] > nums[i] ) dp[i] = max(dp[i],dp[j]+1);
int ret = 0;
for( int i=0;i<n;++i ){
scanf("%d",&nums[i]);
for( int j=0;j<i;++j ){
if ( nums[j] < nums[i] ){
dp[i] = max(dp[i],dp[j] + 1);
}
}
ret = max(dp[i]+1,ret);
}
printf("%d\n",ret);
但是,当 n>=100000 时,很容易超时
所以需要 更高级的算法
设dp[i] i:最长上升子序列长度等于i时,最后一个数字。当然,有可能会更新dp[i]的值。(取最小值)
则有
无后效性:i肯定不会影响i之前的。又因为数组的顺序,就算修改了dpx 的值,也不会影响dp[i]的值。
子问题重叠:需要用二分法来计算当前值为 i 的最后值。
最优子结构:*lower_bound(dp,dp+n,nums[i]) = nums[i];
附 http://poj.org/problem?id=3903 ac代码
#define NMAX 100005
int nums[NMAX];
int dp[NMAX];
int main(){
int n;
while(~scanf("%d",&n)){
memset(dp,0x3f,sizeof(dp));
for( int i=0;i<n;++i ){
scanf("%d",&nums[i]);
*lower_bound(dp,dp+n,nums[i]) = nums[i];
}
int ret = 0;
while(dp[ret] <INF ) ++ret;
printf("%d\n",ret);
}
return 0;
}
注:lower_bound() 函数用于在指定区域内查找不小于目标值的第一个元素。即:有可能等于
upper_bound() 函数用于在指定范围内查找大于目标值的第一个元素。即:只能是大于
所以,如果是使用了函数返回的下标的话,insert它, lower_bound() 会删掉重复的 upper_bound() 则不会。
因题意是,递增子序列,所以用lower_bound
如果是,非下降子序列,则用upper_bound
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 百万级群聊的设计实践
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
· .NET 10 首个预览版发布,跨平台开发与性能全面提升
· 《HelloGitHub》第 107 期