导弹拦截变形(去点最长上升子序列)

8 篇文章 0 订阅

导弹拦截

Description

AA 国为了防御 BB 国的导弹袭击,发展出一种导弹拦截系统。但是这种导弹拦截系统有一个缺陷:虽然它的第一发炮弹能够到达任意的高度,但是以后每一发炮弹都不能高于前一发的高度。因此可能需要配备多套导弹拦截系统才能拦截所有的导弹。

某天,雷达捕捉到 BB 国的导弹来袭。AA 国于是向 CC 国请求援助,CC 国可以在这些导弹进入 AA 国领空前炸掉这些导弹中的某一个。

现在 AA 国想知道,对于每一个导弹,如果 CC 国把它炸掉,那么至少配备多套导弹拦截系统才能拦截剩下所有的导弹。

、
在这里插入图片描述
解题思路:这题是之前导弹拦截系统的一个变形,之前所做的导弹拦截系统使用是最长上分子序列长度=下降子序列个数
而本题要求去点一个导弹之后,需要多少套系统才能够成功拦截这些导弹。
自然还是要求最长上升子序列,删去一个点之后对最长上升子序列的影响要么为0,要么为1
①去掉点i对最长上升子序列没有影响,即点i要么不在最长子序列中,要么就是可以去掉i点后仍可以保持原来长度
②去掉点i对最长上升子序列有影响,影响值为1,即点i是最长子序列中不可取代的一个点
先求以点i结尾的最长子序列长度f,再去求以点i开始的最长子序列g,如果f+g=LIS+1,那么说明i点为最长子序列中的一个结点,如果f的值唯一,那么说明i结点不可被取代,f值已经存在的话,那么说明f值的结点i都是可以被取代的

//注意初始化操作
//求最长上升子序列模版 LDS
//s为待求序列,a[i]保存以i为结尾的最长子序列长度
//dp[i]保存最长子序列长度为i处的元素
memset(dp, 0x3f, sizeof(dp));
for (int i = 1; i <= n; ++i) 
dp[a[i] = (lower_bound(dp + 1, dp + n +1, s[i]) - dp)] = s[i];

//求最长下降子序列 LIS
//s为待求序列 b保存以i开头的最长下降子序列长度
//dp[i]保存最长下降子序列长度为i处的元素
memset(dp, 0x3f, sizeof(dp));
for (int i = n; i >= 1; --i) 
dp[b[i] = (lower_bound(dp + 1, dp + n +1, s[i]) - dp)] = s[i];
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<cstdio>
#include<string.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 1e5 + 10;
#define ll long long 
int n;
int a[maxn];
int b[maxn];
int dp[maxn];
int s[maxn];
int f[maxn];   //记录以元素i结尾的LIS值
int g[maxn];   //记录以元素i开始的LIS值

int LIS;
int vis[maxn];
int mark[maxn];
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &a[i]);
	}
	memset(dp, 0x3f, sizeof(dp));
	for (int i = 1; i <= n; i++) {
		dp[f[i] = (lower_bound(dp + 1, dp + 1 + n, a[i]) - dp)] = a[i];
		LIS = max(LIS, f[i]);
	}
	//求以i点开始的最长上升子序列长度
	memset(dp, -1, sizeof(dp));
	for (int i = 1; i <= n; i++)
		b[i] = a[n - i + 1];
	for (int i = 1; i <= n; i++) {
		dp[s[i] = (lower_bound(dp + 1, dp + 1 + n, b[i], greater<int>() ) - dp)] = b[i];
	}
	for (int i= 1; i <= n; i++)
		g[i] = s[n - i + 1];
	//也可以将原序列取反,然后去求最长下降子序列
	//memset(dp, 0x7f, sizeof(dp));
    //dp[0] = 0;
    //for (int i = n; i >= 0; i--) {
	    //g[i] = lower_bound(dp + 1, dp + n + 1, -a[i]) - tmp;
	    //dp[g[i]] = -a[i];
    //}
	for (int i = 1; i <= n; i++) {
		if (f[i] + g[i] == LIS + 1) {
			vis[f[i]]++;
			mark[i] = 1;
		}
	}
	for (int i = 1; i <= n; i++) {
		if (vis[f[i]] == 1 && mark[i]) 
			printf("%d", LIS - 1);
		else
			printf("%d", LIS);
		if (i == n)
			printf("\n");
		else
			printf(" ");
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Buyi.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值