UVa 1471 Defense Lines

题目描述

在最后一次战争摧毁了你的国家之后,作为阿德尼亚王国的国王,你决定是时候加强首都的防御了。你的防御工事的一部分是一排法师塔,从城市附近开始一直延伸到北部森林。你的顾问们确定防御质量只取决于一个因素:最长的连续递增塔高序列的长度

经过一些艰难的谈判,建造新塔已经不可能了。不过,阿德尼亚的法师们同意拆除一些塔。你可以拆除任意数量的塔,但法师们提出了一个条件:这些塔必须是连续的

例如,如果塔的高度分别是 5,3,4,9,2,8,6,7,15, 3, 4, 9, 2, 8, 6, 7, 15,3,4,9,2,8,6,7,1,那么通过拆除高度为 9,2,89, 2, 89,2,8 的塔,最长的连续递增塔序列是 3,4,6,73, 4, 6, 73,4,6,7。

题目分析

问题理解

我们需要在删除一段连续塔楼后,找到剩余塔楼序列中最长的连续递增子序列的长度。关键约束是:

  • 只能删除一段连续的塔楼(可以不删除)
  • 删除后剩下的塔楼保持原来的相对顺序
  • 需要找到最长的连续递增子序列

输入输出规格

  • 输入 :多个测试用例,第一行是测试用例数量 Z≤25Z \leq 25Z≤25,每个测试用例包含塔的数量 n≤2×105n \leq 2 \times 10^5n≤2×105 和塔的高度序列
  • 输出:每个测试用例输出一个整数,表示通过删除一段连续塔楼后能得到的最长连续递增序列的长度

解题思路

关键观察

  1. 基础情况:如果不删除任何塔楼,答案就是原序列的最长连续递增子序列长度
  2. 连接两个递增序列:通过删除中间的一段塔楼,我们可以将两个递增序列连接起来,条件是前一个序列的最后一个元素小于后一个序列的第一个元素

算法设计

步骤1:预处理数组

定义两个辅助数组:

  • f[i]f[i]f[i]:以第 iii 个塔结尾的最长连续递增子序列长度
  • g[i]g[i]g[i]:以第 iii 个塔开头的最长连续递增子序列长度

计算方式:

  • f[0]=1f[0] = 1f[0]=1,对于 i>0i > 0i>0:如果 a[i]>a[i−1]a[i] > a[i-1]a[i]>a[i−1],则 f[i]=f[i−1]+1f[i] = f[i-1] + 1f[i]=f[i−1]+1,否则 f[i]=1f[i] = 1f[i]=1
  • g[n−1]=1g[n-1] = 1g[n−1]=1,对于 i<n−1i < n-1i<n−1:如果 a[i]<a[i+1]a[i] < a[i+1]a[i]<a[i+1],则 g[i]=g[i+1]+1g[i] = g[i+1] + 1g[i]=g[i+1]+1,否则 g[i]=1g[i] = 1g[i]=1
步骤2:考虑三种情况
  1. 不删除任何塔楼 :答案为 max⁡(f[i])\max(f[i])max(f[i])
  2. 删除单个塔楼 :对于位置 iii,如果 a[i]<a[i+2]a[i] < a[i+2]a[i]<a[i+2],可以连接 f[i]f[i]f[i] 和 g[i+2]g[i+2]g[i+2]
  3. 使用数据结构优化查找 :对于每个位置 iii,找到 j<ij < ij<i 使得 a[j]<a[i]a[j] < a[i]a[j]<a[i] 且 f[j]f[j]f[j] 最大
步骤3:使用 set\texttt{set}set 维护候选解

维护一个 set\texttt{set}set,其中存储 (a[j],f[j])(a[j], f[j])(a[j],f[j]) 对,并保证:

  • 随着 a[j]a[j]a[j] 增加,f[j]f[j]f[j] 也严格增加
  • 这样可以快速二分查找满足 a[j]<a[i]a[j] < a[i]a[j]<a[i] 的最大 f[j]f[j]f[j]

算法复杂度

  • 时间复杂度 :O(nlog⁡n)O(n \log n)O(nlogn),主要来自 set\texttt{set}set 操作
  • 空间复杂度 :O(n)O(n)O(n),用于存储 fff 和 ggg 数组

代码实现

cpp 复制代码
// Defense Lines
// UVa ID: 1471
// Verdict: Accepted
// Submission Date: 2025-10-19
// UVa Run Time: 0.440s
//
// 版权所有(C)2025,邱秋。metaphysis # yeah dot net

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 200005;

int a[MAXN], f[MAXN], g[MAXN];

int main() {
    int Z;
    scanf("%d", &Z);
    while (Z--) {
        int n;
        scanf("%d", &n);
        for (int i = 0; i < n; i++) {
            scanf("%d", &a[i]);
        }
        
        // 计算f[i]: 以i结尾的最长连续递增子序列长度
        f[0] = 1;
        for (int i = 1; i < n; i++) {
            if (a[i] > a[i-1]) f[i] = f[i-1] + 1;
            else f[i] = 1;
        }
        
        // 计算g[i]: 以i开头的最长连续递增子序列长度
        g[n-1] = 1;
        for (int i = n-2; i >= 0; i--) {
            if (a[i] < a[i+1]) g[i] = g[i+1] + 1;
            else g[i] = 1;
        }
        
        int ans = 0;
        // 情况1: 不删除任何塔楼
        for (int i = 0; i < n; i++) {
            ans = max(ans, f[i]);
        }
        
        // 情况2: 删除单个塔楼
        for (int i = 0; i < n - 2; i++) {
            if (a[i] < a[i+2]) {
                ans = max(ans, f[i] + g[i+2]);
            }
        }
        
        // 情况3: 使用set优化查找
        set<pair<int, int>> s; // 存储(a[j], f[j])对
        
        for (int i = 0; i < n; i++) {
            if (i > 0) {
                // 在set中查找满足a[j] < a[i]的最大f[j]
                auto it = s.lower_bound({a[i], -1});
                if (it != s.begin()) {
                    it--;
                    ans = max(ans, it->second + g[i]);
                }
            }
            
            // 维护set,删除被支配的元素
            auto it = s.lower_bound({a[i], -1});
            bool should_insert = true;
            
            // 如果存在相同a值但f值更大的元素,不插入当前元素
            if (it != s.end() && it->first == a[i]) {
                if (it->second >= f[i]) {
                    should_insert = false;
                } else {
                    s.erase(it);
                }
            }
            
            if (should_insert) {
                // 插入当前元素
                it = s.insert({a[i], f[i]}).first;
                
                // 检查前一个元素,如果前一个元素的f值更大,删除当前元素
                if (it != s.begin()) {
                    auto prev_it = it;
                    prev_it--;
                    if (prev_it->second >= it->second) {
                        s.erase(it);
                        should_insert = false;
                    }
                }
                
                // 删除后面被当前元素支配的元素
                if (should_insert) {
                    auto next_it = it;
                    next_it++;
                    while (next_it != s.end() && next_it->second <= it->second) {
                        next_it = s.erase(next_it);
                    }
                }
            }
        }
        
        printf("%d\n", ans);
    }
    return 0;
}

总结

本题的关键在于理解如何通过删除一段连续塔楼来连接两个递增序列,并使用高效的数据结构来优化查找过程。通过预处理 fff 和 ggg 数组,我们可以快速计算基础情况,然后使用set来维护候选解集合,确保在 O(nlog⁡n)O(n \log n)O(nlogn) 时间内解决问题。

相关推荐
不当菜鸡的程序媛24 分钟前
Flow Matching|什么是“预测速度场 vt=ε−x”?
人工智能·算法·机器学习
sali-tec1 小时前
C# 基于halcon的视觉工作流-章58-输出点云图
开发语言·人工智能·算法·计算机视觉·c#
_OP_CHEN1 小时前
算法基础篇:(四)基础算法之前缀和
c++·算法·前缀和·蓝桥杯·acm·icpc·算法竞赛
_OP_CHEN1 小时前
算法基础篇:(五)基础算法之差分——以“空间”换“时间”
c++·算法·acm·icpc·算法竞赛·差分算法·差分与前缀和
DuHz1 小时前
霍夫变换和基于时频脊线的汽车FMCW雷达干扰抑制——论文阅读
论文阅读·物联网·算法·汽车·信息与通信·毫米波雷达
秋风&萧瑟1 小时前
【C++】智能指针介绍
java·c++·算法
QiZhang | UESTC1 小时前
JAVA算法练习题day67
java·python·学习·算法·leetcode
陌路201 小时前
S15 排序算法--归并排序
数据结构·算法·排序算法
智者知已应修善业2 小时前
【c# 想一句话把 List<List<string>>的元素合并成List<string>】2023-2-9
经验分享·笔记·算法·c#·list
B站_计算机毕业设计之家2 小时前
深度学习:python人脸表情识别系统 情绪识别系统 深度学习 神经网络CNN算法 ✅
python·深度学习·神经网络·算法·yolo·机器学习·cnn