UVA 1608:Non-boring sequences(递归+分治思想)

Non-boring sequences

Time limit:3000 ms OS:Linux

点击查看题目内容

题意:

给出一个序列,如果他的任何一个连续的子序列都有至少一个特殊元素(在这个子序列中只出现一次),则这个序列是non-boring,否则是boring

解题思路:

首先要解决的问题是如何快速判断一个子序列是否有特殊元素,这里采用的办法是使用map记录下标为 i 的元素左边最近的相同元素的下标(设为l[i]),和右边最近的相同元素的下标(设为r[i])。
假设子序列的区间是[left,right],只要遍历其中的元素,看是否存在 l[i] < left 并且 r[i] > right 的,如果有就说明这个子序列是中存在特殊元素,就是第 i 个。
由这个特殊元素,我们可以得出[left,i+1],[left,i+2]……和[i-1,right],[i-2,right]……等区间的子序列都包含这个特殊元素。
由此,我们只需要证明[left,i-1]和[i+1,right]这两个区间也包含特殊元素,就可以推出[left,right]这个区间是non-boring了。
按照这个思路写出递归函数:

bool judge(int left,int right)
{
    if(left>=right)
        return true;
    for(int i=left;i<=right;i++)
    {
        if(l[i]<left&&r[i]>right)
            return judge(left,i-1)&&judge(i+1,right);
    }
    return false;
}

看上去一点毛病都没有,但就是超时。看了大神的题解之后才知道原来可以 从两端同时遍历 i(而不是从单一的从左往右) ,这样就可以每次把子问题缩小一半的复杂度,从而优化时间。


Code:

#include <iostream>
#include <algorithm>
#include <map>
#include <cstdio>
using namespace std;

const int maxn= 200000+5;
int a[maxn];
int l[maxn],r[maxn];

map<int,int> m;
int n;

bool judge(int left,int right)
{
    if(left>=right)
        return true;
    for(int i=0;i<=(right-left)/2;i++)
    {
        if(l[left+i]<left && r[left+i]>right)
            return judge(left,left+i-1)&&judge(left+i+1,right);
        if(l[right-i]<left && r[right-i]>right)
            return judge(left,right-i-1)&&judge(right-i+1,right);
    }
    return false;
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        for(int i=0;i<n;i++)
            scanf("%d",a+i);
        //记录第i个元素左边第一个相同元素的位置
        m.clear();
        for(int i=0;i<n;i++)
        {
            if(m.count(a[i]))
                l[i]=m[a[i]];
            else
                l[i]=-1;
            m[a[i]]=i;
        }
        //记录第i个元素右边第一个相同元素的位置
        m.clear();
        for(int i=n-1;i>=0;i--)
        {
            if(m.count(a[i]))
                r[i]=m[a[i]];
            else
                r[i]=n;

            m[a[i]]=i;
        }
        if(judge(0,n-1))
            cout<<"non-boring"<<endl;
        else
            cout<<"boring"<<endl;
    }
    return 0;
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值