【较难,技巧题】动态规划 [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要加头文件(vs中不会出错,但是oj会bug)

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

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

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

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

#include
#include
#include//第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<


你可能感兴趣的:(dp)