UVA 1608 Non-boring sequences (分冶+递归)
一、题目概述
二、题目释义
要求序列s的所有子序列,若所有这些子序列中均存在一个自己序列内是唯一的数,则称这个序列s是不无聊的
三、思路分析
从序列s开始考虑,序列s的整个长度为n,若序列s中存在一个唯一数,则所有包含了这个数的子序列都是不无聊的,那么有可能不满足的要求的子序列只会存在于这个唯一数的左区间与有区间,那么问题就被分冶为了看左区间与右区间内是否有一个唯一数,若找到了,则继续这样递归下去,直到靠近区间长度为1为止
那么我们怎么样做到快速的查找在区间【l,r】内存在着唯一数呢,我们容易想到可以从两端向中间遍历,这样可以将复杂度整体 / 2,但是我们还缺少一个找到唯一数的办法,遍历只是纯粹的搜索并不能帮助我们判断,那么这么判断呢,用数组来保存?不存在的(手动滑稽)。这里可以有一个非常巧妙的预处理,我们当我们找到一个唯一数时,我们总是去继续检索它的左右区间,也就意味着这个唯一数是在它的左区间不曾出现过,在它的右区间也不曾出现过,那么我们可以为序列s的每一个 i 设置两个表示它们的左右区间是否有和它一样的数 pre[] 与 nex[],若没有则左区间记为一个极小数,右区间则记录为一个极大数,若存在则记录那个最靠近它的数的位置。这个记录过程可以由map映射来完成。
四、AC代码
#include <iostream> #include <cstdio> #include <map> using namespace std; const int N = 2e5+5; int a[N]; int pre[N],nxt[N]; map<int,int> mp; inline bool is_unique(int p,int l,int r) { return pre[p]<l && nxt[p]>r; } bool check(int l, int r) { if(l>=r) return 1; for(int d=0; l+d<=r-d; d++) { if(is_unique(l+d,l,r)) return check(l,l+d-1) && check(l+d+1,r); if(l+d == r-d) return 0; if(is_unique(r-d,l,r)) return check(l,r-d-1) && check(r-d+1,r); } return 0; } int main() { int T,n; scanf("%d",&T); while(T--) { scanf("%d",&n); int flag = 0; mp.clear(); for(int i=0; i<n; i++) { scanf("%d",&a[i]); if(!mp.count(a[i])) pre[i] = -1; else pre[i] = mp[a[i]]; mp[a[i]] = i; } mp.clear(); for(int i=n-1; i>=0; i--) { if(!mp.count(a[i])) nxt[i] = n; else nxt[i] = mp[a[i]]; mp[a[i]] = i; } for(int i=1; i<n; i++) { if(a[i] == a[i-1]) { flag = 1; break; } } if(flag) printf("boring\n"); else { if(check(0,n-1)) printf("non-boring\n"); else printf("boring\n"); } } return 0; }