poj-1836 最长上升/下降子序列

        题意:输入一串数字代表着一列排好队的军人的身高,现在要求从其中移走一些人,使得剩余在队伍中的任何一个人都可以向左或者向右看到队伍的头。队伍中身高并列最高的几个人不会相互阻碍,但是其余的身高相同的人会相互阻碍视线,求最少要移出多少人才能满足要求。
       首先理解一下题意,抛开背景,题目要求在一列数中移出最少的数,使得从左到右先是递增,到一个顶点后一直递减。最大的顶点可以连续有几个相同大小的,其余的数必严格递增或者递减。这样,我们可以把题目转化为最长上升/下降子序列来处理,将上升和下降的过程分两次来做。组合后找出最长的先上升后下降的序列,答案就是用原队伍人数减去最长先上升后下降子序列长度。(需要考虑只递增无递减或者只递减无递增的情况)
       先来求最长上升序列。a[i]表示第i个数的大小。用b[i]表示第i个数作为最大值时,最长上升序列数量是多少。则b[i+1]值为1~j(1<=j<=i)数里小于a[i+1]且b[i]值最大的数加一。表示为 b[i] = MAX(b[1~j])+1,(j<i,a[i]>a[j])。代码如下:
b[1]=1;
	for(int i=2;i<=n;i++)
	{
		int max = 1;
		for(int j=1;j<i;j++)
		{
			if(a[i]>a[j]&&b[j]+1>max)
			{
				max = b[j]+1;
			}
		}
		b[i]=max;
	}

        求最长下降子序列的方法类似,只是求解过程相反,从右向左求最长上升子序列。c[i]表示以第i个数表示最大值时,最长下降子序列的数量。代码如下:
c[n]=1;
	for(int i=n-1;i>=1;i--)
	{
		int max = 1;
		for(int j=n;j>i;j--)
		{
			if(a[i]>a[j] && c[j]+1>max)
			{
				max = c[j]+1;
			}
		}
		c[i]=max;
	}

        现在我们已知了以各点作为顶点的最长上升/下降子序列。以i表示上升序列中最高的点,j表示下降序列中最高的点,由于可以最高点可以有数个相同大小的,所以i和j可能不相同。循环判断各种和j组合i的情况,找出最大值。代码如下:
	int num=0;
	int max=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			if(a[i]==a[j])
			{
				num = b[i]+c[j];
				if(i==j)
				{
					num--;
				}
				if(num>max)
				{
					max = num;
				}
			}
		}
	}	

     下面是我AC的完整代码:
#include<iostream>
#include<math.h>
using namespace std;
int n;
float a[1010],b[1010],c[1010];
int dp[1010][1010];
int main()
{
	cin >> n;
	for(int i=1;i<=n;i++)
	{
		cin >> a[i];
	}
	b[1]=1;
	for(int i=2;i<=n;i++)
	{
		int max = 1;
		for(int j=1;j<i;j++)
		{
			if(a[i]>a[j]&&b[j]+1>max)
			{
				max = b[j]+1;
			}
		}
		b[i]=max;
	}
	
	c[n]=1;
	for(int i=n-1;i>=1;i--)
	{
		int max = 1;
		for(int j=n;j>i;j--)
		{
			if(a[i]>a[j] && c[j]+1>max)
			{
				max = c[j]+1;
			}
		}
		c[i]=max;
	}
	int num=0;
	int max=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=i;j<=n;j++)
		{
			if(a[i]==a[j])
			{
				num = b[i]+c[j];
				if(i==j)
				{
					num--;
				}
				if(num>max)
				{
					max = num;
				}
			}
		}
	}	
	cout << n-max << endl;
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值