单调子序列包含有单调递增子序列和递减子序列,不失一般性,这里只讨论单调递增子序列。首先,从定义上明确我们的问题。给定序列a1, a2, …, an,如果存在满足下列条件的子序列
ai1<=ai2<=…<=aim, (其中i1<i2<…<im)
template<typename T>
int LMS (const T* data,int
size)
...
{
if (size<=0)
return0;
T* S=new T[size];
int S_Count=1;
S[0]= data[0];
for (int i=1; i< size; i++)
...{
const T& e = data[i];
int low=0, high= S_Count-1;
while (low<= high)
...{
int mid= (low + high)/2;
if (S[mid]== e)
break;
elseif (S[mid]> e)
...{
high= mid-1;
}
else
...{
low= mid+1;
}
}
//well, in this point
//high is -1, indicating e is the smallest element.
//otherwise, high indicates index of the largest element that is smaller than e
if (high == S_Count-1)
S[S_Count++]= e;
else
S[high+1]= e;
}
return S_Count;
}
初看这个方法有点费解(这个算法转载于http://blog.youkuaiyun.com/fflush/article/details/1503841)
这里我举一个例子说明这个算法
data数组为1 3 5 7 9 5 5 6
S数组的变化依次是
(1)->(1,3)->(1,3,5)->(1,3,5,7)->(1,3,5,7,9)->(1,3,5,5,9)->(1,3,5,5,5)->(1,3,5,5,5,6)
最后得到的是(1,3,5,5,5,6)此数组对应每一项元素是L1,L2,L3,L4,L5,L6
而最长递增序列为6(注意S数组没有存一个可行解)
POJ 2544这个题的代码如下
#include <cstdio>
#define SIZE 1010
using namespace std;
int main()
{
int i, j, n, top, temp;
int s[SIZE];
scanf("%d",&n);
top = 0;
s[0]=-1;
for (i = 0; i < n; i++){
scanf("%d",&temp);
if (temp >s[top])
s[++top] = temp;
else{
int low = 1, high = top;
int mid; //查找第一个大于等于temp的,因为要找单调增。 如果要求找单调不减的,要找第一个大于temp的
while(low <= high)
{
mid = (low + high)>>1;
if (temp>s[mid])
low = mid + 1;
else
high = mid - 1;
}
s[low] = temp;
}
}
printf("%d\n",top);
return 0;
}
HDU 3998
//lower_bound() O(logn) 查找第一个不小于k的元素 upper_bound() O(logn) 查找第一个大于k的元素
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
#define INF 100000
#define MAXN 100000
int dp[MAXN],data[MAXN],mark[MAXN];
int main()
{
int N,i,j,s,num,maxlen;
while(scanf("%d",&N)!=EOF)
{
for(i=0;i<N;i++)
{
scanf("%d",&data[i]);
}
memset(mark,0,sizeof(mark));
s=0;
num=1;
while(1)
{
memset(dp,1,sizeof(dp));
maxlen=-INF;
for(i=0;i<N;i++)
{
if(mark[i])continue;
j=lower_bound(dp,dp+N,data[i])-dp; // 如果是求最长单调不减序列,则此处换为upper_bound();
dp[j]=data[i];
if(j>maxlen)
{
maxlen=j;
mark[i]=1;
}
}
if(s<maxlen+1)s=maxlen+1;
else if(s==maxlen+1)num++;
else break;
}
printf("%d\n%d\n",s,num);
}
return 0;
}
HDU 3998 这个题的第二问也可以最大流去做,可以看做是求最多不相交路径

本文探讨了如何找到一个序列中的最长单调递增子序列,通过举例解释了一种算法的过程,并提供了POJ 2544和HDU 3998两个问题的代码实现。此外,提到HDU 3998的第二问也可通过最大流方法解决。
1019

被折叠的 条评论
为什么被折叠?



