1 #include<cstdio> 2 #include<stdlib.h> 3 #include<algorithm> 4 #define INF 10000000000 5 #define LL long long 6 using namespace std; 7 8 int a[100001] = { 0 }; 9 LL dp[100001]; 10 11 int main(void) { 12 int n; 13 14 scanf("%d", &n); 15 for (int i = 0; i < n; i++) 16 scanf("%d", &a[i]); 17 18 fill(dp, dp + n, INF); 19 /*与memset()函数的区别: 20 两者都可以用来对数组填充,memset是对按照字节来填充的, 21 所以一般用来填充char型数组,也经常用于填充int型的全0或全 - 1操作 22 fill是按照单元来填充的,所以可以填充一个区间的任意值。*/ 23 for (int i = 0; i < n; i++) 24 *lower_bound(dp, dp + n, a[i]) = a[i]; 25 //上面这步,主要是不断插入a[i]到dp中:在满足找到大于等于dp[]的第一个位置时 26 //因为dp中存的是最长上升子序列的子元素 27 //lower_bound()函数求出来的 dp[n-1]是最长上升序列,所以要减一下。。 28 printf("%d\n", n - (lower_bound(dp, dp + n, INF) - dp)); 29 return 0; 30 }
这个插入的方式我举一个例子,比如4 2 3 1 5
因为所有的元素都是一个很大的值,而lower_bound()函数是找到大于等于参数的第一个位置
那么第一次插入地点在下标为0的地方
i | 0 | 1 | 2 | 3 | 4 |
dp[i] | 4 | ∞ | ∞ | ∞ | ∞ |
之后,插入2的时候,因为dp[0] 就已经大于2了,所以用2替换掉4
i | 0 | 1 | 2 | 3 | 4 |
dp[i] | 2 | ∞ | ∞ | ∞ | ∞ |
插入3,可知在下标为1时,插入
i | 0 | 1 | 2 | 3 | 4 |
dp[i] | 2 | 3 | ∞ | ∞ | ∞ |
之后插入1,发现在第一个位置,即可替换,
i | 0 | 1 | 2 | 3 | 4 |
dp[i] | 1 | 3 | ∞ | ∞ | ∞ |
最后插入5
i | 0 | 1 | 2 | 3 | 4 |
dp[i] | 1 | 3 | 5 | ∞ | ∞ |
有点费劲,我举个例子
你比如 4是第一个元素,那之后如果碰到比4还要大的数,是不是直接添加到4之后就可以。那如果4 和这个比4大的数 之间有比4小的,那直接相当于添加在4后面,把原本的数覆盖。
比如 4 5 之后如果是2,那么在dp中 4 5 就相当于 2 5 ,其实也就是这个意思:
比如dp中的4 可以被2取代,因为如果碰到 比2大的可以直接加在2后面覆盖掉5,比如4 5 2 3,那就是2 3
,碰到比4大的,也就直接加 那第一个存的就是2了,比如4 5 2 6,那就是2 5 6。 主要是长度就是最长上升序列,跟里面的元素关系不是很大,里面的元素是最长序列元素
就可以不断放进去,这样既保留了原来的dp最大长度,又可以内部更新
额,,大致是这么个意思,就是不断更新dp,自己也理解的不是很清楚,只是有点想法先记录下。