1187. 导弹防御系统

发布时间 2023-07-01 17:08:01作者: 逐夜之光

为了对抗附近恶意国家的威胁,R国更新了他们的导弹防御系统。

一套防御系统的导弹拦截高度要么一直 严格单调 上升要么一直 严格单调 下降。

例如,一套系统先后拦截了高度为 3 和高度为 4 的两发导弹,那么接下来该系统就只能拦截高度大于 4 的导弹。

给定即将袭来的一系列导弹的高度,请你求出至少需要多少套防御系统,就可以将它们全部击落。

输入格式

输入包含多组测试用例。

对于每个测试用例,第一行包含整数 n,表示来袭导弹数量。

第二行包含 n 个不同的整数,表示每个导弹的高度。

当输入测试用例 n=0 时,表示输入终止,且该用例无需处理。

输出格式

对于每个测试用例,输出一个占据一行的整数,表示所需的防御系统数量。

数据范围

1n50

输入样例:

5
3 5 2 4 1
0 

输出样例:

2

样例解释

对于给出样例,最少需要两套防御系统。

一套击落高度为 3,4 的导弹,另一套击落高度为 5,2,1的导弹。

 

题解

由于题目给了两种选择,每个数即可加入单调上升的序列,也可以加入单调下降的序列,且我们无法得知哪一种方案更好,因此需要枚举所有状态。

所以,本题不适用动态规划。

整体思路是,枚举每个数加入的序列种类,但是这样的复杂度是2的n次方,指数级复杂度,可以用最优化剪枝优化。

举个例子,如果将一个数加入了上升序列,那么最优的放法,就是放入允许的最大的末尾序列元素后面,这样可以使后面的队列元素不会有更差的放法;

用数学语言描述就是,对于所有的上升序列尾部元素集合{up},当元素 x>up[i]且x<=up[i+1]时,使up[i]=x;

然后在dfs过程中,如果当前结果已经大于等于目前的最优解,可以直接ruturn剪掉这一部分。(参考了他人的思路和代码)

代码

 

#include <bits/stdc++.h>
#define ll long long
#define PII pair<int,int> 
using namespace std;

int n,ans=50;
int a[100],up[100],down[100];
void dfs(int x,int u,int d)
{
	if(u+d>=ans) return;
	if(x==n+1){
		ans=min(ans,u+d);
		return;
	}
	
	int k = 0;
	while (k < u && up[k] >= a[x]) k++;
	int num = up[k];
	up[k] = a[x];
	if (k < u) dfs(x + 1, u, d);
	else dfs(x + 1, u + 1, d);
	up[k] = num;
	
	k = 0;
	while (k < d && down[k] <= a[x]) k++;
	num = down[k];
	down[k] = a[x];
	if (k < d) dfs(x + 1, u, d);
	else dfs(x + 1, u , d + 1);
	down[k] = num;
}
void solve()
{
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	ans=50;
	dfs(1,0,0);
	cout<<ans<<endl;
}
int main()
{
	int T=1;
	//cin>>T; 
	while(1)
	{
		cin>>n;
		if(n==0) break;
		else solve();
	}
	return 0;
}