poj 1836 Alignment

// 题意:战士们站成一排,要求每个人都能看到在他左边或右边的所有人,
// 只要有另外一人不比他矮,他就无法看到尽头了,问要至少出队多少人,即是求满足条件的最长子序列
// 比如 1 2 3 4 4 3 2 1 就满足条件,但 1 2 3 3 3 2 1 不满足,因为中间有一个3不能看到左边或右边的尽头。
// 分成两半,左半部分是最长严格上升子序列,范围[1-i],长度为Len1;
// 而右半部分是最长严格下降子序列,范围[i+1-n],长度为Len2
// 需要用 LIS算法 从前往后跑一遍,然后从后往前再跑一遍
// 思路:枚举 i (1<=i<=n),利用LIS算法计算出Len1和Len2,取两者的和的最大值,最后用总人数减掉,就是需要出队的最少人数了

#include<iostream> //最长严格上升子序列算法
using namespace std;

const int MAXN=1002;
int n;
void lis(double arr[],double seq[],int num[])
{
seq[1]=arr[1];
num[1]=1;
int rear=1;
for(int i=2;i<=n;++i)
{
if(arr[i]>seq[rear])
{
seq[++rear]=arr[i];
}
else
{
int s=1,t=rear,mid;
while(s<t)
{
mid=(s+t)/2;
if(seq[mid]<arr[i])
s=mid+1;
else
t=mid;
}
seq[s]=arr[i];
}
num[i]=rear; //num[i]是记录arr[1-i]的最长严格上升子序列的长度
}

}
double arr1[MAXN],seq1[MAXN],arr2[MAXN],seq2[MAXN]; //是double类型
int num1[MAXN],num2[MAXN];

int main()
{
cin>>n;
for(int i=1;i<=n;++i)
{
cin>>arr1[i];
arr2[n+1-i]=arr1[i]; //翻转数组后再求上升子序列,相当于对原序列求下降子序列
}

lis(arr1,seq1,num1); //两次求最长严格上升子序列
lis(arr2,seq2,num2);

int res=0;
for(int i=0;i<=n;++i) //枚举中间点
res=max(res,num1[i]+num2[n-i]); //由原序列arr1 的i+1->n 映射到翻转序列arr2 的1->n-i
cout<<n-res<<endl;

return 0;
}

posted on 2011-07-22 20:07  sysu_mjc  阅读(113)  评论(0编辑  收藏  举报

导航