D - Pyramid 最长上升子数组(连续子序列)
题目大意
给定一个数组 \(a\),求经过以下操作可以得到的最长的金字塔序列。
金字塔序列形如 \(1,2,3,4,5,...,k-1,k,k-1,...5,4,3,2,1\)。
可进行的操作为:
- 将某数减1
- 将开头或末尾的数移除
900分左右的题目。我先口胡一下我想到的做法,首先二分长度 \(k\),然后每次检查的时候开一个线段树,每次这个长度为 \(2\times k-1\) 的区间往后移动的时候其实是可以进行区间加 / 减贡献的,然后每次我检查这个区间有没有小于 \(0\) 的数就可以了,复杂度外面二分加上线段树 \(O(NlogNlogN)\),线段树需要实现区间加和查找第一个小于 \(0\) 的数,并不好写而且很明显这个不是正解。
考虑 \(l_i\) 代表 \(i\) 往左最多能走多少,同理 \(r_i\) 代表 \(i\) 最多能往右走多少,那么答案就是 \(\forall i,\ max(min(l_i,\ r_i))\)。算\(l_i, \ r_i\) 的时候相当于利用了一个 \(dp\) 的思想,\(l_i=min(l_{i-1}+1, \ a_i),\ r_i=min(r_{i+1},\ a_i)\)。
感觉做思维题太少一下想不到简单的方法,虽然能乱搞搞过去但是很浪费时间。
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
int n = nextInt();
int[] a = new int[n+1];
for (int i = 1; i <= n; i++) a[i] = nextInt();
int[] l = new int[n+1], r = new int[n+2];
for (int i = 1; i <= n; i++) l[i] = Math.min(l[i-1] + 1, a[i]);
for (int i = n; i >= 1; i--) r[i] = Math.min(r[i+1] + 1, a[i]);
int ans = 0;
for (int i = 1; i <= n; i++) ans = Math.max(ans, Math.min(l[i], r[i]));
cout.println(ans);
closeAll();
}
public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
public static int nextInt() throws Exception{ cin.nextToken(); return (int) cin.nval; }
public static void closeAll() throws Exception { cout.close(); in.close(); out.close(); }
}

浙公网安备 33010602011771号