UVa1471 Defense Lines (滑动窗口)

链接:http://bak.vjudge.net/problem/UVA-1471

分析:设g(i)为以第i个元素结尾的最长L序列长度,f(i)为以第i个元素开头的最长L序列长度,首先在O(n)时间内求出f(i)和g(i),枚举完j和i之后,最长L序列的长度就是g(j)+f(i)|j<i,A[j]<A[i]。

这样做的时间复杂度是O(n²)。还可以优化的更好,只枚举i,不枚举j,使用STL中的set,set容器可以看成是排好序的,而且自带lower_bound和upper_bound函数,把所有“有保留价值”的j按照A[j]从小到大排成一个有序表(用二元组(A(j),g(j))保存,以A[j]为关键字),维护g(j)根据A[j]严格单调递增,那么用二分查找找到满足A[j]<A[i]的最大的A[j],g(j)也是最大的。

每次枚举一个i,二分找到A[j]<A[i]最大的g(j)更新ans,接下来就是维护单增,如果g(j)>=g(i),准备要插入的二元组(A[i],g[i])肯定不是最优值,放弃将其插入有序表;否则将其插入有序表后,向前位置已经保证是单增的了,只需向后检查,删去肯定不是最优值的二元组,还有A[j]相同的j只保留最优的一个(虽然直接删掉旧的就可以过,但我没搞懂)。

 1 #include <cstdio>
 2 #include <set>
 3 using namespace std;
 4
 5 const int maxn = 200000 + 5;
 6
 7 struct Candidate {
 8     int a, g;
 9     Candidate(int a, int g):a(a),g(g) {}
10     bool operator < (const Candidate& rhs) const {
11         return a < rhs.a;
12     }
13 };
14
15 set<Candidate> s;
16 int n, a[maxn], f[maxn], g[maxn];
17
18 int main() {
19     int T;
20     scanf("%d", &T);
21     while (T--) {
22         scanf("%d", &n);
23         for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
24         if (n == 1) { printf("1\n"); continue; }
25         g[1] = 1;
26         for (int i = 2; i <= n; i++)
27             if (a[i - 1] < a[i]) g[i] = g[i - 1] + 1;
28             else g[i] = 1;
29         f[n] = 1;
30         for (int i = n - 1; i >= 1; i--)
31             if (a[i] < a[i + 1]) f[i] = f[i + 1] + 1;
32             else f[i] = 1;
33         s.clear();
34         s.insert(Candidate(a[1], g[1]));
35         int ans = 1;
36         for (int i = 2; i <= n; i++) {
37             Candidate c(a[i], g[i]);
38             set<Candidate>::iterator it = s.lower_bound(c);
39             bool keep = true;
40             if (it != s.begin()) {
41                 Candidate last = *(--it);
42                 int len = f[i] + last.g;
43                 ans = max(ans, len);
44                 if (c.g <= last.g) keep = false;
45             }
46             if (keep) {
47                 ++it;
48                 if (it -> a == c.a && it -> g >= c.g) continue;
49                 s.erase(c);
50                 s.insert(c);
51                 it = s.find(c);
52                 it++;
53                 while (it != s.end() && it->a > c.a && it->g <= c.g) s.erase(it++);
54             }
55         }
56         printf("%d\n", ans);
57     }
58     return 0;
59 }
时间: 2024-12-24 12:49:43

UVa1471 Defense Lines (滑动窗口)的相关文章

uva1471 Defense Lines

#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizoef(a)) using namespace std; typedef long long ll; const int maxn=1000100; const int INF=1<<29; int a[maxn],n; int f[maxn],g[maxn]; struct Node {

【二分】Defense Lines

[UVa1471] Defense Lines 算法入门经典第8章8-8 (P242) 题目大意:将一个序列删去一个连续子序列,问最长的严格上升子序列 (N<=200000) 试题分析:算法1:直接暴力,对于一个删除序列,枚举头和尾,然后看最长上升子序列.时间复杂度:O(N^3) 算法2:L[i]表示以i为结尾的最长严格上升子序列长度,R[i]表示以i为开头的最长严格上升子序列长度. 预处理:O(N)  然后依旧是枚举头和尾,那么答案就是L[i]+R[j]了.时间复杂度:O(N^2) 算法3:第

详解--单调队列 经典滑动窗口问题

单调队列,即单调的队列.使用频率不高,但在有些程序中会有非同寻常的作用. 动态规划·单调队列的理解 做动态规划时常常会见到形如这样的转移方程: f[x] = max or min{g(k) | b[x] <= k < x} + w[x] (其中b[x]随x单调不降,即b[1]<=b[2]<=b[3]<=...<=b[n]) (g[k]表示一个和k或f[k]有关的函数,w[x]表示一个和x有关的函数) 这个方程怎样求解呢?我们注意到这样一个性质:如果存在两个数j, k,使

单调队列(滑动窗口问题)

Description An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards b

uva 1606 amphiphilic carbon molecules【把缩写写出来,有惊喜】(滑动窗口)——yhx

Shanghai Hypercomputers, the world's largest computer chip manufacturer, has invented a new classof nanoparticles called Amphiphilic Carbon Molecules (ACMs). ACMs are semiconductors. It meansthat they can be either conductors or insulators of electro

tcp滑动窗口与拥塞控制

TCP协议作为一个可靠的面向流的传输协议,其可靠性和流量控制由滑动窗口协议保证,而拥塞控制则由控制窗口结合一系列的控制算法实现.一.滑动窗口协议     所谓滑动窗口协议,自己理解有两点:1. "窗口"对应的是一段可以被发送者发送的字节序列,其连续的范围称之为"窗口":2. "滑动"则是指这段"允许发送的范围"是可以随着发送的过程而变化的,方式就是按顺序"滑动".在引入一个例子来说这个协议之前,我觉得很有必

滑动窗口的中位数

2017年8月7日 19:46:26 难度:困难 描述:给定一个包含 n 个整数的数组,和一个大小为 k 的滑动窗口,从左到右在数组中滑动这个窗口,找到数组中每个窗口内的中位数.(如果数组个数是偶数,则在该窗口排序数字后,返回第 N/2 个数字.) 样例: 对于数组 [1,2,7,8,5], 滑动大小 k = 3 的窗口时,返回 [2,7,7] 最初,窗口的数组是这样的: [ | 1,2,7 | ,8,5] , 返回中位数 2; 接着,窗口继续向前滑动一次. [1, | 2,7,8 | ,5],

CodeForces 279B Books (滑动窗口)

题意:给定n本书的阅读时间,然后你从第 i 本开始阅读,问你最多能看多少本书在给定时间内. 析:就是一个滑动窗口的水题. 代码如下: #pragma comment(linker, "/STACK:1024000000,1024000000") #include <cstdio> #include <string> #include <cstdlib> #include <cmath> #include <iostream>

UVa 12174 Shuffle (滑动窗口)

题意:你正在使用的音乐播放器有一个所谓的乱序播放功能,即随机打乱歌曲的播放顺序.假设一共有s首歌, 则一开始会给这s首歌随机排序,全部播放完毕后再重新随机排序.继续播放,依次类推.注意,当s首歌播放完毕之前不会重新排序. 这样,播放记录里的每s首歌都是1~s的一个排列.给出一个长度为n的1≤s,n≤100000)的播放记录(不一定是从最开始记录的)xi(1≤xi≤s), 你的任务是统计下次随机排序所发生的时间有多少种有多少种可能性. 例如,s=4,播放记录是3,4,4,1,3,2,1,2,3,4