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

题目

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

思路

枚举方法,假设一种情况:
序号: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;
}
阅读更多
文章标签: 预处理 技巧枚举
个人分类: 3.aoapc2ch8习题
上一篇[枚举] UVa1312 球场 (离散化)(技巧枚举典例)
下一篇[复杂模拟] UVa10366 龙头滴水(需要一定的推理能力)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭