您的位置:首页 > 理论基础 > 计算机网络

2013杭电网络赛第一场 1005 Deque

2013-07-23 21:35 337 查看


Deque

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 0 Accepted Submission(s): 0

Problem Description

Today, the teacher gave Alice extra homework for the girl weren't attentive in his class. It's hard, and Alice is going to turn to you for help.
The teacher gave Alice a sequence of number(named A) and a deque. The sequence exactly contains N integers. A deque is such a queue, that one is able to push or pop the element at its front end or rear end. Alice was asked to take out the elements from the sequence in order(from A_1 to A_N), and decide to push it to the front or rear of the deque, or drop it directly. At any moment, Alice is allowed to pop the elements on the both ends of the deque. The only limit is, that the elements in the deque should be non-decreasing.
Alice's task is to find a way to push as many elements as possible into the deque. You, the greatest programmer, are required to reclaim the little girl from despair.


Input

The first line is an integer T(1≤T≤10) indicating the number of test cases.
For each case, the first line is the length of sequence N(1≤N≤100000).
The following line contains N integers A1,A2,…,AN.


Output

For each test case, output one integer indicating the maximum length of the deque.


Sample Input

3
7
1 2 3 4 5 6 7
5
4 3 2 1 5
5
5 4 1 2 3


Sample Output

7
5
3


Source

2013 Multi-University Training Contest 1


题目大意:给定一个序列,从前往后向一个双端队列中加入元素,使队列中的元素承递增,求最多能放多少个数字。

解题思路:DP

DP经典问题合唱队形问题和这题基本上是相同的,只是这题的数据范围太大,不能用正常的O(n2)的求最长上升子序列的方法来做。这里需要用到一种O(n*logn)的方法。

最长不下降子序列的O(n*logn)算法分析如下:

设 A[t]表示序列中的第t个数,F[t]表示从1到t这一段中以t结尾的最长上升子序列的长度,初始时设F [t] = 0(t = 1, 2, ..., len(A))。则有动态规划方程:F[t] = max{1, F[j] + 1} (j = 1, 2, ..., t - 1, 且A[j] < A[t])。

现在,我们仔细考虑计算F[t]时的情况。假设有两个元素A[x]和A[y],满足

(1)x < y < t (2)A[x] < A[y] < A[t] (3)F[x] = F[y]

此时,选择F[x]和选择F[y]都可以得到同样的F[t]值,那么,在最长上升子序列的这个位置中,应该选择A[x]还是应该选择A[y]呢? 很明显,选择A[x]比选择A[y]要好。因为由于条件(2),在A[x+1] ... A[t-1]这一段中,如果存在A[z],A[x] < A[z] < a[y],则与选择A[y]相比,将会得到更长的上升子序列。

再根据条件(3),我们会得到一个启示:根据F[]的值进行分类。对于F[]的每一个取值k,我们只需要保留满足F[t] = k的所有A[t]中的最小值。设D[k]记录这个值,即D[k] = min{A[t]} (F[t] = k)。

注意到D[]的两个特点:

(1) D[k]的值是在整个计算过程中是单调不上升的。

(2) D[]的值是有序的,即D[1] < D[2] < D[3] < ... < D


利 用D[],我们可以得到另外一种计算最长上升子序列长度的方法。设当前已经求出的最长上升子序列长度为len。先判断A[t]与D[len]。若A [t] > D[len],则将A[t]接在D[len]后将得到一个更长的上升子序列,len = len + 1, D[len] = A [t];否则,在D[1]..D[len]中,找到最大的j,满足D[j] < A[t]。令k = j + 1,则有A [t] <= D[k],将A[t]接在D[j]后将得到一个更长的上升子序列,更新D[k] = A[t]。最后,len即为所要求的最长上 升子序列的长度。

在 上述算法中,若使用朴素的顺序查找在D[1]..D[len]查找,由于共有O(n)个元素需要计算,每次计算时的复杂度是O(n),则整个算法的 时间复杂度为O(n^2),与原来的算法相比没有任何进步。但是由于D[]的特点(2),我们在D[]中查找时,可以使用二分查找高效地完成,则整个算法 的时间复杂度下降为O(nlogn),有了非常显著的提高。需要注意的是,D[]在算法结束后记录的并不是一个符合题意的最长上升子序列!

/*************************************************************************************/

讲的非常好,但是没怎么看懂,呵呵呵。

不过D数组的确是很有用的。D[k]表示当前扫过的数字中最长不下降子序列长度为k的数字是多少。虽然不知道为什么如果找到两个元素F[x] = F[y],且A[x] < A[y] < A[t],非得要选A[x],当时如上方法所说算出的D[k]的确是单调递增的。这个是可以证明的。

然后利用二分的原理可以很方便的找出当前点之前的最大的子序列的个数,从而降低了这个算法的时间复杂度,很巧妙的方法。

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;

int t,n,ans,temp;
int num[110000];
int up[110000],down[110000];
int du[110000],dd[110000];

int erfenup(int k,int s,int t)
{
if (k < du[s]) return s-1;
int mid=(s+t)/2;
if (s == t) return s-1;
if (s+1 == t) return s;
if (du[mid] > k) return erfenup(k,s,mid);
else   return erfenup(k,mid,t);
}

int erfendown(int k,int s,int t)
{
if (k > dd[s]) return s-1;
int mid=(s+t)/2;
if (s == t) return s-1;
if (s+1 == t) return s;
if (dd[mid] < k) return erfendown(k,s,mid);
else   return erfendown(k,mid,t);
}

int main()
{
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);

scanf("%d",&t);
while (t--)
{
scanf("%d",&n);
for (int i = n ; i >= 1 ; i --)
scanf("%d",&num[i]);
memset(up,0,sizeof(up));
memset(down,0,sizeof(down));
memset(du,120,sizeof(du));
memset(dd,0,sizeof(dd));
up[1] = 1;
down[1] = 1;
du[0] = 1;
du[1] = num[1];
dd[0] = 1;
dd[1] = num[1];
for (int i = 2; i <= n ; i ++)
{
if (num[i] >du[du[0]]) {
du[0]++;
du[du[0]] = num[i];
up[i] = du[0];
}
else  {
temp = erfenup(num[i],1,du[0]);
temp++;
up[i] = temp;
du[temp] = min(du[temp],num[i]);
}
if (num[i] <dd[dd[0]]) {
dd[0]++;
dd[dd[0]] = num[i];
down[i] = dd[0];
}
else  {
temp = erfendown(num[i],1,dd[0]);
temp++;
down[i] = temp;
dd[temp] = max(dd[temp],num[i]);
}
}
ans = 0;
for (int i = 1 ; i <= n ; i ++)
ans = max(ans,up[i]+down[i]);
printf("%d\n",ans-1);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: