洛谷P1020 导弹拦截

时间:2023-01-30 18:44:23

链接

  https://www.luogu.org/problem/show?pid=1020

题解

  第一问,直接DP。

  关于第二问:就是用最少的链串起所有的点。

  考虑新加入一个元素,假设前面的已经用最少的链串起来了。那么当前点该加到哪一条链上?

  如果我们记录了前面每一个点的next(即所在链的下一个点)。如果next为空,且这个点的权值≥新加入点的权值,就可以直接把新加入的点串到后面,这肯定是最优的选择。

  但如果扫描了前面所有的点,都没找到上述情况的话,我可以新开辟一条链,并以当前点为首点;还有一种选择,把前面一条链断开,把当前点接上去,这样的话链数同样增加了1。不难发现,这两种决策是等价的。因为后面再来点的时候,接到这两条链的末端造成的情形是一样的,在这两条链的某处断开,造成的情形也是一样的。所以说这两种操作没有什么区别...,因此我们就可以不考虑断链的决策,只是在后面添加或者开辟新链,这样这道题就很好写了。记录链数和每条链末端的权值,每次来一个点就扫描所有的末端,如果找到合适的末端就把那个末端的权值改成当前点的权值,否则新建一个末端,权值等于当前点权值。

  时间复杂度O(N^2)(其实可以优化但没必要)

代码

//dp
#include <cstdio>
#include <algorithm>
#define maxn 1000
using namespace std;
int h[maxn], f[maxn], N, ans, list[maxn];
int main()
{
	int i, j;
	for(N=1;~scanf("%d",h+N);N++)
	{
		f[N]=1;
		for(i=1;i<N;i++)
		{
			if(h[N]<=h[i])f[N]=max(f[i]+1,f[N]);
		}
		ans=max(ans,f[N]);
	}
	printf("%d\n",ans);
	N--;
	for(ans=0,i=1;i<=N;i++)
	{
		for(j=1;j<=list[0];j++)
		{
			if(h[i]<=list[j])
			{
				list[j]=h[i];
				break;
			}
		}
		if(j>list[0])list[++list[0]]=h[i];
	}
	printf("%d",list[0]);
	return 0;
}