UVa 1608,Non-boring sequences

好诡异的一个题啊

紫书上关于从左边找还是从两边往中间找的讨论没有看懂,怎么一下就找到唯一的元素了(⊙_⊙?)

方法就是用的书上讲的方法,类似于uva 11572,不过这个题需要预处理存下两边的最近的相同数的位置

for (int i=1;i<=n;i++) {
            prev[i]=r[a[i]];
            next[prev[i]]=i;
            r[a[i]]=i;}//记录元素a[i]上次出现的位置,因为是从左向右遍历,所以上次出现的位置正好是prev[i]要求的
        //prev[i],与 i位置的元素  相同的左边最近的元素的位置
        //next[i] 同理 

然后递归检查是否符合题意就可以了。。。

从左边开始找

int dfs(int l,int r){
    if (l>=r) return 1;
    int p;
    for (p=l;p<=r;p++)
        if (next[p]>r&&prev[p]<l) break;
    if (p>r) return 0;
    return dfs(l,p-1)&&dfs(p+1,r);
}
//TLE的dfs,从左往右找的

TLE,然后改成从两边向中间开始找

int dfs(int l, int r) {
    if(l >= r)    return 1;
    for(int i = 0; i <= (r-l+1)/2; i++) {
        if(next[l+i] > r && prev[l+i] < l)
            return dfs(l, l+i-1) && dfs(l+i+1, r);
        if(next[r-i] > r && prev[r-i] < l)
            return dfs(l, r-i-1) && dfs(r-i+1, r);
    }
    return 0;
} 

但是还是TLE,这下我就蛋疼了,想半天想不出来还能优化的地方

结果参考网上大神,对比了一下,发现他是用的map存的,避免了使用memset,但是换成了map.clear()(map.clear非常快么= =)

下面那条只是把map改成数组,然后只用了一句memset,看时间增加量

AC代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <map>
#define maxn 200000+5
using namespace std;
int prev[maxn],next[maxn];
int a[maxn];
map <int,int>r;
int n;

/*
int dfs(int l,int r){
    if (l>=r) return 1;
    int p;
    for (p=l;p<=r;p++)
        if (next[p]>r&&prev[p]<l) break;
    if (p>r) return 0;
    return dfs(l,p-1)&&dfs(p+1,r);
} */
//TLE的dfs,从左往右找的

int dfs(int l, int r) {
    if(l >= r)    return 1;
    for(int i = 0; i <= (r-l+1)/2; i++) {
        if(next[l+i] > r && prev[l+i] < l)
            return dfs(l, l+i-1) && dfs(l+i+1, r);
        if(next[r-i] > r && prev[r-i] < l)
            return dfs(l, r-i-1) && dfs(r-i+1, r);
    }
    return 0;
} //二分的思想,从两边往中间找
int main()
{
    int testcase;
    scanf("%d",&testcase);
    while (testcase--){
        //memset(prev,0,sizeof(prev));
        //memset(next,0,sizeof(next));
        //memset(a,0,sizeof(a));
        //memset(r,0,sizeof(r));
        r.clear();
        scanf("%d",&n);
        for (int i=1;i<=n;i++) {scanf("%d",&a[i]);prev[i]=0;next[i]=n+1;}
        for (int i=1;i<=n;i++) {
            prev[i]=r[a[i]];
            next[prev[i]]=i;
            r[a[i]]=i;}//记录元素a[i]上次出现的位置,因为是从左向右遍历,所以上次出现的位置正好是prev[i]要求的
        //prev[i],与 i位置的元素  相同的左边最近的元素的位置
        //next[i] 同理
         puts(dfs(1, n) ? "non-boring" : "boring");
    }
}

时间复杂度是o(nlogn)

以后对于大数据量的数组,还是尽量不要用memset这条语句了,虽然是线性的......

ps.本题代码有参考网上某大神的代码。我一开始写的代码比较丑,尤其是预处理那段,贴出来也没人看得懂= =,就不贴了。

时间: 2024-08-25 14:37:38

UVa 1608,Non-boring sequences的相关文章

uva 1608 不无聊的序列(附带常用算法设计和优化策略总结)

uva 1608 不无聊的序列(附带常用算法设计和优化策略总结) 紫书上有这样一道题: 如果一个序列的任意连续子序列中都至少有一个只出现一次的元素,则称这个序列时不无聊的.输入一个n个元素的序列,判断它是不是无聊的序列.n<=200000. 首先,在整个序列中找到只出现一次的元素ai.如果不能找到,那它就是无聊的.不然,就可以退出当前循环,递归判断[1, i-1]和[i+1, n]是不是无聊的序列.然而怎么找ai很重要.如果从一头开始找,那么最差情况下的时间复杂度就是O(n^2)的.而如果从两头

uva 11825 ,Hacker&#39;s Crackdown 状态压缩 dp

// uva 11825 Hacker's Crackdown // // 题目意思看了很久才看懂,有n台计算机,有n种服务,每台计算机上运行所有 // 的服务,并且其中有的计算机与某些计算机相互邻接,对于每台计算机, // 你可以选择一项服务,停止这项服务,则与它邻接的计算机的该服务也停止了 // 你的目的是让经量多的服务完全瘫痪 // // 换而言之,这个问题就是在n个集合中(p[1]....p[n])分成尽量多的组数,使得每组 // 的并集等于全集(即所有的n台电脑都停止)... // /

POJ 1504,ZOJ 2001,UVA 713, Adding Reversed Numbers,错误,已找到错误

------------------------------------------------------------ 以此题警告自己: 总结, 1.在数组的使用时,一定别忘了初始化 2.在两种情况复制代码时,一定要小心,注意修改变量名,一不留神就会带来不可估量的后果,一定要仔细挨着一个一个变量的修改,别跳着看着哪个变量就改哪一个变量! (这个题目中,就是复制了一下,代码,ca,我找了一下午的错....还好终于找到了,一个字母的错,) -----------------------------

UVA 1608 Non-boring sequence 不无聊的序列(分治,中途相遇)

题意:给你一个长度为n序列,如果这个任意连续子序列的中都有至少出现一次的元素,那么就称这个序列是不无聊的,判断这个序列是不是无聊的. 先预处理出每个元素之前和之后相同元素出现的位置,就可以在O(1)的时间判断出一个元素在一个区间内是否唯一. 每次从大的序列中找一个唯一元素,包含这个元素的就不用判断了,那么以这个元素为分界线,在分别判断两边的序列. 如果只从一遍找的话,最坏的情况是唯一元素在另一头T(n) = T(n-1)+O(n) >= O(n^2)的. 所以应该从两边开始找,T(n) = 2T

UVA 1608 Non-boring sequences

#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=200100; const int INF=1<<29; int n,a[maxn]; int L[maxn],R[maxn]; map<int,int&

UVA - 1608 Non-boring sequences (分治)

题意:如果一个序列的任意连续子序列中至少有一个只出现一次的元素,则称这个序列式为non-boring.输入一个n(n≤200000)个元素的序列A(各个元素均为109以内的非负整数),判断它是否无聊. 分析: 1.记录下每个元素左边和右边最近的与它值相同的元素的位置. 2.如果某个元素在某一序列中只出现过一次,那这个序列中所有包含该元素的子序列都是non-boring,因此只需要研究在这个序列中以该元素为分界线,其左边和右边的子序列是否无聊即可. 3.因为如果单独从左扫或从右扫,最坏情况可能是在

UVa 1608 (分治 中途相遇) Non-boring sequences

预处理一下每个元素左边和右边最近的相邻元素. 对于一个区间[l, r]和区间内某一个元素,这个元素在这个区间唯一当且仅当左右两边最近的相邻元素不在这个区间内.这样就可以O(1)完成查询. 首先查找整个字符串是否有唯一元素,如果没有则整个序列是无聊的. 有的话,假设这个唯一元素下标是p,那么如果子序列[0, p-1]和[p+1, n-1]是不无聊的,那么这个序列就是不无聊的. 关于查找的话,如果只从一端查找唯一元素,最坏的情况就是O(n).所以可以从两端进行查找,总的时间复杂度是O(nlogn).

UVA 1608 Non-boring sequences (分冶+递归)

一.题目概述 二.题目释义 要求序列s的所有子序列,若所有这些子序列中均存在一个自己序列内是唯一的数,则称这个序列s是不无聊的 三.思路分析 从序列s开始考虑,序列s的整个长度为n,若序列s中存在一个唯一数,则所有包含了这个数的子序列都是不无聊的,那么有可能不满足的要求的子序列只会存在于这个唯一数的左区间与有区间,那么问题就被分冶为了看左区间与右区间内是否有一个唯一数,若找到了,则继续这样递归下去,直到靠近区间长度为1为止 那么我们怎么样做到快速的查找在区间[l,r]内存在着唯一数呢,我们容易想

●UVA 1608 Non-boring sequences

题链: https://vjudge.net/problem/UVA-1608#author=chenchonghan题解: 分治 如果一个区间[l,r]里面在p位置出现了一个只出现一次的元素,(如果不存在该元素,该区间就是boring的) 那么必然包含p的子区间都是non-boring的. 即如果存在boring的区间,必然是[l,p-1],[p+1,r]的子区间. 所以继续递归处理上面两个区间即可. (判断某个元素是否在区间里只出现一次,只需考虑它左边第一个与它相同的元素或它右边第一个相同的