NYOJ—17 单调递增最长子序列(DP)
原题链接传送门
这是一道LIS(单调递增最长子序列)模板题。
#include <cstdio>
#include <cstring>
using namespace std;
int dp[10005];
char a[10005];
int Max;
void LIS() {
int len = strlen(a);
Max = 0;
for(int i=0; i<len; i++) {
dp[i] = 1; //把第i个字符的最长递增子序列初始化为1;
for(int j=0; j<i; j++) { //倒着推,求得dp[i] ,0-i的最长递增子序列 ;
if(a[i] > a[j] && dp[i] < dp[j]+1) { //判断i是否大于i之前的字符,如果大于
dp[i] = dp[j] + 1; //并且 dp[i] < dp[j]+1,就更新一下dp[i] ;
}
}
if(Max < dp[i]) //更新字符串a中最长递增子序列 ;
Max = dp[i];
}
printf("%d\n",Max);
}
int main() {
int t;
scanf("%d",&t);
while(t--) {
scanf("%s",a);
LIS();
}
return 0;
}
建议手动模拟一下,容易理解
这道题懂了的话,可以做这一道拦截导弹练练手。
道理都是相同的。
NYOJ—214 单调递增子序列(二) (二分 + DP)
原题链接传送门
同样是道LIS,但是这里再用上面那种DP方法就要TLE了。
为什么呢?由于这道题数据量比较大,而上面那个传统DP方法的时间复杂度是O(n^2),肯定不可行,所以这里就用了二分的方法,时间复杂度就降为O(nlogn)了。
具体怎么写的看代码:
#include <cstdio>
using namespace std;
int a[100005];
int maxv[100005]; //这里用maxv数组来存长度为i的单调递增子序列最大元素的最小值。
int binarysearch(int key,int len){ //二分查找maxv数组中刚好大于a[i]的那个值的位置
int l,r,mid;
l = 0;
r = len;
while(l < r){
mid = l + (r-l)/2;
if(maxv[mid] <= key)
l = mid + 1;
else r = mid;
}
return l;
}
int main() {
int n,i,j,len;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
maxv[0] = a[0];
len = 0;
for(int i=1;i<n;i++){
if(a[i] > maxv[len]){ //当a[i]大于maxv[len]时,直接把a[i]存到maxv[len+1]。
maxv[++len] = a[i]; //(也只有这时才能把a[i]存进maxv;len随之+1);
}
else { //当a[i]小于maxv[len]时,通过二分查找maxv数组0-len中
int pos = binarysearch(a[i],len); //刚刚大于a[i]的那个值的位置pos;
maxv[pos] = a[i]; //更新maxv[pos]为a[i];
}
}
printf("%d\n",len+1); //len即为单调递增最长子序列的长度。
} //因为我是从maxv[0]开始存的,所以这里len要加1;
return 0;
}
同样,还是建议手动模拟一下此过程,这样会更容易理解。
如果不太懂二分查找,可以看张dalao的这篇博客,讲解很详细。