dp+二分(最长不降子序列问题)
题意
题目就是要你求出最长非递减的最长子序列长度
白书(挑战程序设计)中提供两种算法
O
(
n
2
)
和
O
(
n
l
o
g
n
)
O(n^2)和O(nlogn)
O(n2)和O(nlogn)解决该问题
第一种算法用到的知识是dp
d
p
[
i
]
表
示
以
a
[
i
]
结
尾
的
最
长
子
序
列
,
更
新
方
式
是
d
p
[
j
]
−
>
d
p
[
i
−
1
]
(
即
j
从
1
到
i
−
1
)
dp[i]表示以a[i]结尾的最长子序列,更新方式是dp[j]->dp[i-1](即j从1到i-1)
dp[i]表示以a[i]结尾的最长子序列,更新方式是dp[j]−>dp[i−1](即j从1到i−1)
i
f
(
a
[
i
]
>
=
a
[
j
]
)
d
p
[
i
]
=
m
a
x
(
d
p
[
i
]
,
d
p
[
j
]
+
1
)
if(a[i]>=a[j]) dp[i]=max(dp[i],dp[j]+1)
if(a[i]>=a[j])dp[i]=max(dp[i],dp[j]+1)
max_size=1;
for(int i=1;i<=n;i++){
dp[i]=1;//单独以a[i]为序列的长度
for(int j=1;j<i;j++){
if(a[i]>=a[j]) dp[i]=max(dp[i],dp[j]+1);
}
}
max_size=max(max_size,dp[i]);
第二种算法用到的知识是二分+思维
15
大
于
12
,
进
入
s
t
a
c
k
里
面
,
只
要
元
素
大
于
等
于
栈
顶
元
素
就
可
以
让
元
素
入
栈
15大于12,进入stack里面,只要元素大于等于栈顶元素就可以让元素入栈
15大于12,进入stack里面,只要元素大于等于栈顶元素就可以让元素入栈
如
果
15
不
大
于
20
的
话
,
我
们
就
需
要
把
17
换
成
15
,
因
为
如
果
子
序
列
的
长
度
相
同
,
那
么
末
位
置
的
元
素
较
小
的
在
之
后
会
更
加
有
优
势
如果15不大于20的话,我们就需要把17换成15,因为如果子序列的长度相同,那么末位置的元素较小的在之后会更加有优势
如果15不大于20的话,我们就需要把17换成15,因为如果子序列的长度相同,那么末位置的元素较小的在之后会更加有优势
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int maxn=1e6+5;
int a[maxn],cnt=0;
int main(){
int n,k;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&k);
if(a[cnt]<=k){
a[++cnt]=k;
}
else{
a[lower_bound(a+1,a+cnt+1,k)-a]=k;
}
}
if(n-cnt<=1){printf("YES\n");}
else{
printf("NO\n");
}
return 0;
}
区间dp+前缀和
题解
s
u
m
表
示
当
前
前
缀
和
sum表示当前前缀和
sum表示当前前缀和
如
果
当
前
加
入
的
数
大
于
前
缀
和
+
1
,
那
么
输
出
前
缀
和
+
1
,
否
则
继
续
。
如果当前加入的数大于前缀和+1,那么输出前缀和+1,否则继续。
如果当前加入的数大于前缀和+1,那么输出前缀和+1,否则继续。
因
为
需
要
表
示
连
续
的
整
数
,
那
么
相
邻
的
数
最
多
只
能
差
1.
因为需要表示连续的整数,那么相邻的数最多只能差1.
因为需要表示连续的整数,那么相邻的数最多只能差1.
如
果
排
序
后
k
前
面
的
数
字
之
和
<
k
−
1
,
那
么
k
−
1
这
个
数
就
无
法
表
示
。
如果排序后k前面的数字之和<k-1,那么k-1这个数就无法表示。
如果排序后k前面的数字之和<k−1,那么k−1这个数就无法表示。
再
详
细
的
说
就
是
再详细的说就是
再详细的说就是
现
在
能
表
示
出
[
0
,
0
]
这
个
区
间
,
那
么
对
于
排
序
后
接
下
来
的
数
k
,
如
果
k
>
1
,
那
么
1
现在能表示出[0,0]这个区间,那么对于排序后接下来的数k,如果k>1,那么1
现在能表示出[0,0]这个区间,那么对于排序后接下来的数k,如果k>1,那么1
这
个
数
就
永
远
也
拼
不
出
来
。
那
么
对
于
之
前
能
拼
出
的
区
间
为
[
0
,
x
]
,
加
上
k
之
后
能
拼
出
这个数就永远也拼不出来。那么对于之前能拼出的区间为[0,x],加上k之后能拼出
这个数就永远也拼不出来。那么对于之前能拼出的区间为[0,x],加上k之后能拼出
的
数
至
少
为
[
k
,
x
+
k
]
,
必
须
要
求
[
0
,
x
]
这
个
区
间
的
右
端
点
和
[
k
,
k
+
x
]
的
左
端
点
连
续
才
能
把
所
有
的数至少为[k,x+k],必须要求[0,x]这个区间的右端点和[k,k+x]的左端点连续才能把所有
的数至少为[k,x+k],必须要求[0,x]这个区间的右端点和[k,k+x]的左端点连续才能把所有
数
都
拼
出
来
,
也
就
是
k
<
=
x
+
1
数都拼出来,也就是k<=x+1
数都拼出来,也就是k<=x+1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
ll a[maxn],sum[maxn];
int n;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
}
sort(a+1,a+n+1);
for(int i=1;i<=n;i++){
if(a[i]>sum[i-1]+1) {cout<<sum[i-1]+1<<endl;return 0;}
sum[i]=sum[i-1]+a[i];
}
cout<<sum[n]+1<<endl;
return 0;
}
优化算法
直接
O
(
n
)
+
优
化
O(n)+优化
O(n)+优化AC
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
using namespace std;
typedef long long ll;
ll cnt=0;
int main(){
ll n;
scanf("%lld",&n);
for(int i=1;i<=n;i++){
ll num=n/i;
i=n/num;
cnt++;
}
cout<<cnt<<endl;
return 0;
}