poj1836

蓝色士兵的身高和红色士兵的身高是完全没有关系的。

 

要求最少出列数,就是留队士兵人数最大,如图,即左边的递增序列人数和右边的递减序列人数之和最大

因而可转化为求“最长不降子序列”和“最长不升子序列”问题

 

但是不能直接套用LIS思想,因为这题不允许任一侧的序列中出现等高士兵

 

方法:

对士兵的身高数组逐一进行枚举,枚举到的k值作为蓝色士兵,k+1值作为红色士兵,以这两个士兵分别作为最长不降子序列L1的终点和最长不升子序列L2的起点,即作为整个队列的分界点。

然后分别对两边进行dp,枚举到某一个m值时,使得L1+L2的长度为最大max,此时用总士兵人数n 减去max就是  最少出列人数

本题只能用LIS的O(n*logn)算法,否则常规加上枚举会达到O(n^3)


源码:

#include<iostream>
using namespace std;
const int inf=3;

//ord[]为不降序列
//二分法搜索digit,若str中存在digit,返回其下标
//若不存在,返回str中比digit小的最大那个数的(下标+1)
int binary_search_1(double ord[],double digit,int head,int length)
{
	int left=head,right=length;
	int mid;
	while(right!=left)
	{
		mid=(left+right)/2;
		if(digit==ord[mid])
			return mid;
		else if(digit<ord[mid])
			right=mid;
		else
			left=mid+1;
	}
	return left;
}

//ord[]为不升序列
//二分法搜索digit,若str中存在digit,返回其下标
//若不存在,返回str中比digit大的最小那个数的(下标+1)
int binary_search_2(double ord[],double digit,int head,int length)
{
	int left=head,right=length;
	int mid;
	while(right!=left)
	{
		mid=(left+right)/2;
		if(digit==ord[mid])
			return mid;
		else if(digit>ord[mid])
			right=mid;
		else
			left=mid+1;
	}
	return left;
}

int main(int i,int j)
{
	int n;  //士兵数
	while(cin>>n)
	{
		double* h=new double[n+1];

		for(i=1;i<=n;i++)
			cin>>h[i];

		int max=0;
		for(int m=1;m<=n;m++)  //对身高队列每一个值作为分界点,进行枚举
		{
			double* ord=new double[n+1];

			/*Dp-(0~m)-LIS*/

			ord[0]=-1;  //下界无穷小
			int len_LIS=1;
			for(i=1;i<=m;i++)
			{
				ord[len_LIS]=inf; //上界无穷大
				j=binary_search_1(ord,h[i],0,len_LIS);
				if(j==len_LIS)  //sq[i]大于ord最大(最后)的元素
					len_LIS++;
				ord[j]=h[i];
			}
			len_LIS--; //减去ord[0]的长度1

			/*Dp-(m+1~n)-LDS*/

			ord[m]=inf;  //下界无穷大
			int len_LDS=1;
			for(i=m+1;i<=n;i++)
			{
				ord[m+len_LDS]=-1; //上界无穷小
				j=binary_search_2(ord,h[i],m,m+len_LDS);
				if(j==m+len_LDS)  //sq[i]大于ord最小(最后)的元素
					len_LDS++;
				ord[j]=h[i];
			}
			len_LDS--;  //减去ord[m]的长度1

			//max为对于当前m的 最长不升子序列LIS 和 最长不降子序列LDS 长度之和

			if(max<len_LIS+len_LDS)
				max=len_LIS+len_LDS;

			delete ord;
		}

		cout<<n-max<<endl;

		delete h;
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值