Description
给定一个数列,求最长上升子序列长度
Input
多组测试数据,每组数据给出1 ≤ n ≤ 103和1 ≤ b ≤ 104,表示n个数a1, a2, …, an.
其中 a1 = b,ai = (ai − 1+1)2mod(109+7), i > 1.
Output
最长上升子序列长度.
Sample Input
10 5Sample Output
8Hint
获得数组参考代码:
a[1] = b;
for(int i = 2; i <= n; i ++) {
a[i] = 1LL * (a[i - 1] + 1) * (a[i - 1] + 1) % mod;
}
最长上升子序列(Longest Increasing Subsequence,简称LIS)是指在一个序列中,找到一个最长的子序列使得这个子序列中的所有元素都是单调递增的 。
如【1, 7, 3, 5, 9, 4, 8】,最长上升的子序列是【1,3,5,9】或者【1,3,4,8】
动态规划解法:
假设原序列为a[1…n],LIS的长度为dp[1…n]。对于每一个位置i,我们维护一个状态dp[i],表示以a[i]作为结尾的LIS的长度。
初始时,对于所有的i,dp[i]都初始化为1。因为对于任意的i,长度为1的LIS只有自己。
然后从i=2开始,对于每一个位置i,我们遍历所有小于i的位置j,如果a[j] < a[i],则说明a[i]可以接在以a[j]结尾的LIS后面,从而形成一个新的LIS
转移方程:dp[i] = max(dp[j] + 1),其中 j < i 且 a[j] < a[i]
#include<iostream>
#include<cstring>
#include<math.h>
using namespace std;
int nums[1000000];
int dp[1000000];
int main()
{
int mod = pow(10, 9) + 7;
int n, b;
cin >> n >> b;
nums[1] = b;
for (int i = 2; i <= n; i++) {
nums[i] = 1LL * (nums[i - 1] + 1) * (nums[i - 1] + 1) % mod;
}
//初始化dp为1
for (int i = 0; i < 1000000; i++) { dp[i] = 1; }
for (int i = 1; i <= n; i++)
{
for (int j = 1; j < i; j++)
if (nums[i] > nums[j])
dp[i] = max(dp[j]+1, dp[i]);
}
int ans = 0;
for (int i = 1; i <=n; i++) ans = max(ans, dp[i]);
cout << ans << endl;
return 0;
}
时间复杂度为O(n^2),当数据量大时可能会超时
贪心算法和二分查找:
我们维护一个数组d,其中d[i]表示长度为i的LIS的最后一个元素的最小可能取值。初始时,d中只有d[0] = -inf一个元素。
接下来,我们从左到右遍历原序列a,对于每个元素a[i],我们找到在d中最大的小于a[i]的元素d[j],并将d[j+1]更新为a[i]。如果找不到这样的j,则说明a[i]比d中的所有元素都大,此时我们将d中的最后一个元素d[len(d)-1]更新为a[i]。最终,d中非负元素的个数就是原序列的LIS的长度。
#include<iostream>
#include<cstring>
#include<math.h>
#include<vector>
using namespace std;
int nums[1000000];
int main()
{
int mod = pow(10, 9) + 7;
int n, b;
cin >> n >> b;
vector<int> ans;
nums[1] = b;
for (int i = 2; i <= n; i++) {
nums[i] = 1LL * (nums[i - 1] + 1) * (nums[i - 1] + 1) % mod;
}
ans.push_back(nums[0]);
for (int i = 1; i <= n; i++)
{
// 如果当前数值大于已选结果的最后一位,则直接往后新增,若当前数值更小,则直接替换前面第一个大于它的数值
if (nums[i] > ans.back())
ans.push_back(nums[i]);
else
{
int left = 0, right = ans.size() - 1;
//二分查找
while (left < right)
{
int mid = (left+right)>>1;
if (ans[mid] < nums[i])
{
left = mid + 1;
}
else
{
right = mid;
}
}
ans[left] = nums[i];
}
}
cout << ans.size()-1 << endl;
return 0;
}