NYOJ 17 214 单调递增子序列
转移方程:dp[i]=dp[j]+1(a[i]>a[j]),1(a[i]<=a[j])
边界条件:dp[i]=1;
dp[j]记录a[1..j]中子序列的最长个数,dp[i]记录a[1..i]中子序列的最长个数,
当a[i]>a[j](j取值范围:0..i-1)时,dp[i]+1,
否则dp[i]=1即a[0..i-1]没有比a[i]更小的元素。
代码入下:
#include<stdio.h>
#include<string.h>
int p[10001];
char str[10001];
int main()
{
int m,max;
scanf("%d",&m);
while(m--)
{
int i,j,len;
scanf("%s",str);
len=strlen(str);
p[0]=1;
for(i=1,max=1;i<len;i++)
{
p[i]=1;
for(j=i-1;j>=0;j--)
if(str[i]>str[j]&&p[i]<p[j]+1)
p[i]=p[j]+1;
if(max<=p[i]) max=p[i];
}
printf("%d\n",max);
}
return 0;
}
法二:利用规律
dp[len+1]=dp[len]+1(a[len+1]>a[len])
dp[len+1]=dp[j<len]+1(a[len+1]>a[j]&&a[len+1]<=a[len])在法一求解过程中可以发现,a[i]>a[i-1]时,dp[i]=dp[i-1]+1,当a[i]<=a[i-1]时,寻找第一个比a[i]小的元素位置 j,并取dp[i]=dp[j]+1,否则dp[i]=1
若用一数组表示,则相当于取得tmp[j]<a[i],tmp[j+1]位置被置为a[i]。tmp[j+1]被置为a[i]后,相当于将tmp[j+1]的值缩小,为寻找最长子序列提供保障。
比如a[i+1]>tmp[len] 则tmp[len+1]=a[i+1],len++;
若a[i+1]>tmp[j<len] 则tmp[j+1]=a[i+1],len不变。
(此处数组tmp用来存储子序列,但最后得到的存储内容,非最长子序列,需添加辅助数组。)
#include<iostream>
#include <string>
//#include <time.h>
using namespace std;
int main()
{
//freopen("1.txt","r",stdin);
int n ;
cin>>n;
while(n--)
{
string str;
int count=1;
cin>>str;
int a[200],p[200];
a[0]=-999;
for (int i=0;i<str.length();i++)
{
for (int j=count-1;j>=0;j--)
{
if((int)str[i]>a[j])
{
a[j+1]=str[i];
p[j+1]=i;
if(j+1==count) count++;
break;
}
}
}
for(int i=1;i<count;i++)
cout<<(char)a[i]<<' '<<p[i]<<'\n';
cout<<count-1<<endl;
}
法三:借用STL中的lower_bound函数
lower_bound实例:
// lower_bound/upper_bound example
#include <iostream> // std::cout
#include <algorithm> // std::lower_bound, std::upper_bound, std::sort
#include <vector> // std::vector
int main () {
int myints[] = {10,20,30,30,20,10,10,20};
std::vector<int> v(myints,myints+8); // 10 20 30 30 20 10 10 20
std::sort (v.begin(), v.end()); // 10 10 10 20 20 20 30 30
std::vector<int>::iterator low,up;
low=std::lower_bound (v.begin(), v.end(), 20); // ^
up= std::upper_bound (v.begin(), v.end(), 20); // ^
std::cout << "lower_bound at position " << (low- v.begin()) << '\n';
std::cout << "upper_bound at position " << (up - v.begin()) << '\n';
return 0;
}
运行结果:
lower_bound at position 3
upper_bound at position 6
lower_bound函数源码:
template <class ForwardIterator, class T>
ForwardIterator lower_bound (ForwardIterator first,
ForwardIterator last, const T& val)
{
ForwardIterator it;
iterator_traits<ForwardIterator>::difference_type count, step;
count = distance(first,last);
while (count>0)
{
it = first; step=count/2; advance (it,step);
if (*it<val) { // or: if (comp(*it,val)), for
version (2)
first=++it;
count-=step+1;
}
else count=step;
}
return first;
}法三源码:
#include<stdio.h>
#include<algorithm>
using namespace std;
const int MAX=100100;
int num[MAX],top;
int main()
{
int n,i,*p;
while(scanf("%d",&n)!=EOF)
{
scanf("%d",&num[0]);
top=1;
for( i=1;i<n;i++)
{
scanf("%d",&num[i]);
p=lower_bound(num,num+top,num[i]);//寻找
if(p-num==top) ++top;
*p=num[i];//插入
}
//for(i=0;i<top;i++)
//printf("%d ",num[i]);
printf("%d\n",top);
}
}
法四:二分查找
#include<stdio.h>
int a[100005];
int dp[100005];
int binarysearth(int x,int len)
{
int left=1,right,mid;
right=len;
mid=(left+right)/2;
while(left<=right)
{
if(x>dp[mid])
{
left=mid+1;
}
else
if(x<dp[mid])
right=mid-1;
else
return mid;
mid=(left+right)/2;
}
return left;
}
int main()
{
int n;
while(~scanf("%d",&n))
{
int i,j,len;
for(i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
len=1;
dp[1]=a[0];
for(i=1;i<n;i++)
{
j=binarysearth(a[i],len);
dp[j]=a[i];
if(j>len)
len=j;
}
printf("%d\n",len);
}
return 0;
}
单调子序列基本求解方法总结完毕。
在此基础上,可了解区间问题。
本文介绍四种求解最长单调递增子序列的方法:直接动态规划、利用规律优化、使用STL函数以及二分查找法,并附带详细代码实现。
504

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



