【较难,技巧题】动态规划 [NOIP1999]拦截导弹 ——求最大下降子序列长和最小覆盖全部数据的序列数

点击打开链接(新发现的一个oj,样式很小清新呀)

题目大意:n个导弹,(1)求最大非上升子序列长度(2)要拦截所有导弹最少要配备这种导弹拦截系统的套数

思路1:每一次求最大非上升子序列,求一次删一次。直到数组全0。有点害怕超时,然而没有hhh

注意:

1、需要记录序列不能用O(nlogn)的方法
2、lower_bound()函数的要求是查找数组是非递减的,找第一个>=x的位置;upper_bound(a.begin(),a.end(),x)求求非递减数组中第一个>x的位置
3、剔除元素后,内外循环都要判断a[i]==0
4、使用memset要加头文件<string.h>(vs中不会出错,但是oj会bug)

5、剔除元素判断必须用dp[j],用pre[j]最后一个元素剔除不了

思路2:(2)是求最大上升子序列(参考,虽然没很懂,但是记住这个定理吧,可以套模板)

两个定理:定理1 令(X,≤)是一个有限偏序集,并令r是其最大链的大小。则r为X的最大反链数。
                 对偶定理:对于一个偏序集,其最少反链划分数等于其最长链的长度。这就是Dilworth定理。

思路1代码:(思路2就不敲了。。。)

#include<iostream>
#include<algorithm>
#include<string.h>//第n+1次忘了这个。。。
using namespace std;
int a[1001],pre[1001],dp[1001]={0};
int main()
{
	int i,k,M,Mindex,j,count,n=0;
	cin>>k;
	for (i=1;i<=k;i++)
		cin>>a[i];
	count=k;
	while(count)//数组中非0个数
	{
		n++;
		M=0;
		memset(dp,0,sizeof(dp));
		memset(pre,0,sizeof(dp));
		for (i=k;i>=0;i--)
		{
			if (a[i]==0)continue;//忽略已删除元素
			for (j=k;j>i;j--)
			{
				if (a[j]==0) continue;//不能忘记!!!!!里面也要判断一次
				if ( a[j]<=a[i] && dp[j]>dp[i])
				{
					dp[i]=dp[j];
					pre[i]=j;//记录序列
				}
			}
			dp[i]++;
			if (dp[i]>M)
			{
				M=dp[i];
				Mindex=i;
			}
		}
		if (count==k)//输出第一次求得的序列长
			cout<<M<<" ";
		j=Mindex;
		while(dp[j])//剔除本次求得的序列元素,不能用pre[j]
		{
			a[j]=0;
			j=pre[j];
			count--;
		}
	}
	cout<<n<<endl;
	return 0;
}


猜你喜欢

转载自blog.csdn.net/Always_ease/article/details/80548749