[枚举] UVa1618 弱键 (预处理)

题目

给出k(最大5000)个不同整数组成的序列,判断是否存在4个整数 Np,Nq,Nr,Ns(1<=p<q<r<s<=k) N p , N q , N r , N s ( 1 <= p < q < r < s <= k ) ,使得 Np<Ns<Nq<Nr N p < N s < N q < N r 或者 Np>Ns>Nq>Nr N p > N s > N q > N r

思路

枚举方法,假设一种情况:
序号:p,q,r,s
大小:q>s>p>r


1.首先枚举p。原因是p序号最小,都能用得上H[maxn], L[maxn]。
2. 再枚举q,q在H[p]中枚举。
3. 再确定r,在L[p]中查找第一个序号大于q的,即为r。原因是,r的大小对最后s的枚举没影响,而r的序号应越小越好,才能给s的枚举腾出更多序号可选空间。(贪心)
4. 最后枚举s,s满足在L[q]和H[p]的交集中,且满足序号小于r。


全部枚举完,就可以return true。
理论复杂度为O(n*nO(n3),陈锋的这个方法个人感觉并不快,但还是AC了。


本题感觉,就在于技巧枚举,如何枚举使r不需要枚举,减少一个次方的复杂度,就很好写了。另外一个,vector数组在数据不多的题目的运用。

代码

#include <cstdio>
#include <cstdlib>
#include <vector>
#include <algorithm>
#define _for(i,a,b) for(int i = (a); i<(b);i++)
#define _rep(i,a,b) for(int i = (a); i<=(b);i++)
using namespace std;

int readin() { int x; scanf("%d", &x); return x; }  // 简化版输入 
const int maxn = 5000 + 100;
int n, A[maxn];
vector<int> H[maxn], L[maxn];  // 存储的是下标 
                               //higher和lower, 表示点i后面比它大的元素组成的序列,比它小的元素组成的序列

bool solve1(int p) {
    for (vector<int>::iterator q = L[p].begin(); q != L[p].end(); ++q) {
        vector<int>::iterator r = lower_bound(H[p].begin(), H[p].end(), *q);
        if (r == H[p].end()) continue;
        vector<int>::iterator s = lower_bound(L[p].begin(), L[p].end(), *r);
        while (s != L[p].end()) {
            if (binary_search(H[*q].begin(), H[*q].end(), *s)) return true;
            ++s;
        }
    }
    return false;
}

bool solve2(int p) {
    for (vector<int>::iterator q = H[p].begin(); q != H[p].end(); ++q) {
        vector<int>::iterator r = lower_bound(L[p].begin(), L[p].end(), *q);
        if (r == L[p].end()) continue;
        vector<int>::iterator s = lower_bound(H[p].begin(), H[p].end(), *r);
        while (s != H[p].end()) {
            if (binary_search(L[*q].begin(), L[*q].end(), *s)) return true;
            ++s;
        }
    }
    return false;
}

void init() {
    _for(i, 0, n) {
        L[i].clear();
        H[i].clear();
    }
}

int main() {
    int T = readin();
    while (T--) {
        n = readin();
        _for(i, 0, n) A[i] = readin();

        // 对H, L进行打表
        init();
        _for(i, 0, n)
            _for(j, i + 1, n)
            if (A[j] > A[i]) H[i].push_back(j);
            else if (A[j] < A[i]) L[i].push_back(j);

        // 首先对P进行枚举,因为P的序号最小,才能用得上H和L
        bool success = false;
        _for(p, 0, n)
            if (solve1(p) || solve2(p)) {
                success = true;
                break;
            }
        if (success) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值