题意:给定一个数组,求该数组中严格最长递增子序列的长度和相应的最长子序列的个数,在求最长子序列的有多少种时,数组中每一个元素只能使用一次
思路:1. 求最长递增子序列的长度时,我们使用二分查找的方法求,具体的操作是维护一个辅助数组,这个辅助数组dp[k]中保存着当前已经获得最长递增子序列,k表示当前的辅助数组中递增序列的个数,当处理原数组中下一个元素data[i]时,
1)若data[i] > dp[k - 1]时, 直接dp[k++] = data[i];
2) 否则,使用二分查找在dp中找到第一个不大于data[i]的元素的位置j, 使得dp[j] = data[i];
这样当遍历结束整个原数组时,我们也就找到了其中一个最长递增子序列, 最长递增子序列的长度必然也就知道了。
2. 此处我们还要求若存在最长递增子序列时,原数组中每一个元素只能使用一次,即该元素在最长递增子序列A中出现后,就不能在其他的最长子序列中在出现了,问有多少种?
这个时候我们只需用一个标记数组,对原序列的每个元素进行标记就行了,多次重复步骤1),直到没有满足最长递增子序列的子序列出现。
hdu 3998 Sequence 源代码如下
#include <iostream>
#include <algorithm>
using namespace std;
#define N 11000
int data[N];
int dp[N];
bool flag[N];
int binarySearch(int a[], int n, int key)
{
int left = 0, right = n - 1;
int mid;
while(left <= right)
{
mid = left + (right - left ) / 2;
if (a[mid] == key)
{
return mid;
}
else if (a[mid] < key)
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return left;
}
int main()
{
int i, j, n, num, maxLen, k;
while(scanf("%d", &n) != EOF)
{
for (i = 0;i < n; ++i)
{
scanf("%d", &data[i]);
}
memset(flag, 0, sizeof(flag));
k = 0;
maxLen = 0;
num = 0;
while(true)
{
k = 0;
for (i = 0;i < n; ++i)
{
if (flag[i])
{
continue;
}
if (dp[k - 1] < data[i])
{
dp[k++] = data[i];
flag[i] = true;
}
else
{
j = binarySearch(dp, k, data[i]);
dp[j] = data[i];
}
}
if (maxLen < k)
{
maxLen = k;
num = 1;
}
else if (maxLen == k)
{
++num;
}
else
{
break;
}
}
printf("%d\n%d\n", maxLen, num);
}
return 0;
}