最长不下降子序列总结

P1020 [NOIP1999 普及组] 导弹拦截

洛谷原题

题目描述

某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。某天,雷达捕捉到敌国的导弹来袭。由于该系统还在试用阶段,所以只有一套系统,因此有可能不能拦截所有的导弹。

输入导弹依次飞来的高度,计算这套系统最多能拦截多少导弹,如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

输入格式
1行,若干个整数

输出格式
2行,每行一个整数,第一个数字表示这套系统最多能拦截多少导弹,第二个数字表示如果要拦截所有导弹最少要配备多少套这种导弹拦截系统。

分析:第一问为求最长不上升子序列,用dp实现
第二问贪心 ,用结构体维护每组导弹所能拦截的最小值,因为每次都要求出所有导弹系统能拦截的最小值中的最小值来进行判断能否用现有的系统兼容(最优地)

阴间代码:

#include<bits/stdc++.h>

const int maxn=505;
using namespace std;
int n,a[maxn],f[maxn];
int ans=-maxn;
int len=1,flag;

struct node
{
	int d[105],top,lenz=0;
}e[105];

bool cmp(node xxx,node yyy)
{
	return xxx.top<=yyy.top;
}

int main()
{
	while(~scanf("%d",&a[++n])){}
	for(int i=1;i<=n;++i)f[i]=1;
	n--;
	for(int i=2;i<=n;++i)
	{
		for(int j=1;j<i;++j)
		{
			if(a[i]<=a[j])
			{
				f[i]=max(f[i],f[j]+1);
			}
		}
	}
	for(int i=1;i<=n;++i)ans=max(ans,f[i]);
	printf("%d\n",ans);
	//d[len]=a[1];
	e[len].top=a[1];
	e[len].d[++e[len].lenz]=a[1];
	for(int i=2;i<=n;++i)
	{
		flag=0;
		sort(e+1,e+1+len,cmp);
		for(int j=1;j<=len;++j)
		{
			if(e[j].top>=a[i])
			{
				e[j].top=a[i];
				e[j].d[++e[j].lenz]=a[i];
				flag=1;
				break;
			}
		}
		if(flag==0)
		{
			e[++len].top=a[i];
			e[len].d[++e[len].lenz]=a[i];
		}
	}
	printf("%d",len);
	return 0;
}

另:(模版)最长不下降子序列
1.dp

#include<bits/stdc++.h>

using namespace std;
int a[10100],f[10100],n,maxn,ans[10100],num;

void print(int x)
{
	if(!x) return;
	print(ans[x]);
	printf("%d ",a[x]);
}

int main() 
{
	while(scanf("%d",&a[++n])!=EOF)
	{
	}
	for(int i=1;i<=n;i++) 
	{
		f[i]=1;
	}
	{
	n--;
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i-1;j++)
		{
			if(a[i]>a[j]) 
			{
				if(f[i]<f[j]+1)
				{
					f[i]=f[j]+1;
					ans[i]=j;//类似于图论的写法,存当前的上一位(链)
				}
				if(f[i]>maxn)
				{
					num=i;
					maxn=f[i];
				}
			}
		}
	}
	printf("max=%d\n",maxn);
	print(num);
	return 0;
}

注意:
有两个转移方程:
f[i]=max(f[i-1],f[j]+1) ------1
f[i]=max(f[i],f[j]+1) -------2
第一个方程f[i]求的是序列前i个中最长的不下降子序列长度,所以最后输出结果直接输出f[n]
第二个方程f[i]求的是这个点(i)能形成的最长不下降子序列,最后要有循环判出 i=1~n;ans=max(ans,f[i]);

2.模拟+二分 O(nlogn)

#include<bits/stdc++.h>

const int maxn=1005;
using namespace std;
int d[maxn],a[maxn],len;
int n;

/*int find(int st,int ed,int v)
{
	int ret=ed;
	while(st<=ed)
	{
		int mid=(st+ed)/2;
		if(d[mid]>=v)
		{
			ret=mid;
			ed=mid-1;
		}
		else st=mid+1;
	}
	return ret;
}*/

int main()
{
	scanf("%d",&n)	;
	for(int i=1;i<=n;++i)scanf("%d",&a[i]);
	d[1]=a[1];
	len=1;
	for(int i=2;i<=n;++i)
	{
		if(a[i]>=d[len])d[++len]=a[i];
		else
		{
			int temp=upper_bound(d+1,d+len+1,a[i])-d;//最长不下降
			//int temp=find(l,len,a[i])
			d[temp]=a[i];
		}
	}
	printf("%d",len);
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值