[SMOJ1827]删除数

97 篇文章 0 订阅
8 篇文章 0 订阅

题目描述

N 个整数,从左往右排成一行,现在你要删除其中的若干个数,不妨假设剩下的数,从左往右,重新标号为:
a1,a2,a3,a4,,ak
那么必须使得: a2a1=a3a2=a4a3==aiai1
例如: N=5 ,这 5 个数是: 1 8 4 7 10。
那么删除 8 之后,剩下的数是:1 4 7 10,可以发现: 4-1 = 7-4 = 10-7。
你的任务是计算:在满足上述条件下,最多可以剩下多少个数?

输入格式 1827.in

第一行,一个整数 N 1N2000
第二行, N 个整数。每个整数范围:[0,109]。 有接近50%的数据,每个整数的范围是: [0,1000]

输出格式 1827.out

一个整数。

输入样例 1827.in

5
1 4 3 5 7

输出样例 1827.out

4

样例解释

删除4后,剩下的1 3 5 7满足题意。


既然 a2a1=a3a2=a4a3==aiai1 ,那么也就意味着删除后的序列必然是一个等差数列,而且要注意递增和递减都是有可能的。

可以看到 n2000 ,比较小, n2 的复杂度是可以支持得住的,不妨从这里入手。
在最终的等差数列中,如果我们通过枚举确定了前两项 ai aj ,也就得到了该数列的公差 d 。于是就可以往后不断查找 aj+d aj+2d ……直到找不到为止。这样便可以得到以 ai aj 开头的数列长度,枚举完之后就可以得到答案了。

上面这个做法中,前面的枚举并没有问题,因为至少要确定两个量才能确定一个等差数列。但后面的查找部分,如果再用一个 O(n) j 的后面扫一遍,时间复杂度就达到了 O(n3),很显然是会 TLE 的。我们知道,如果 j 后面有满足条件的 ak=aj+d,那么实际上 (j,k) 区间内的数都是要被删除的。既然如此,那我们为什么还要花时间在这之中扫描呢?

其实,我们在确定了 ai aj 之后的关键就是,快速判断是否有 ak ,满足 k>j ak=aj+(ajai) 。如果有再迭代,直至找到最后一个满足条件的 k <script type="math/tex" id="MathJax-Element-1179">k</script>。
这其实是很满足我们对于 hash 的要求的。

不妨在 hash 表中记两个值,原始数字和位置。读入时就将每个数标记好。之后要判断时直接在 hash 表中找就可以了。但是要注意,这题用拉链法会更好。
请设想,数字的范围较大,冲突的可能性实际上是比较小的。如果用线性探测法,可能还要找比较多次。但是拉链法则可以将同 hash 值的聚在一起,更方便。

此外还要注意细节:要记得开 long long,答案最小是取1,处理越界等问题。

参考代码:

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <vector>

using namespace std;

typedef long long LL;

const int maxn = 2000 + 10;
const int prime = 1000007;

struct Data { LL value; int info; }; //数值和位置

int n;
LL a[maxn];


#define inf 1000000000 //最大值,越界就不必考虑了

vector <Data> hash[prime];

void insert1(LL val, int pos) { //往 hash 表中插入值
    int y = (val + inf) % prime;
    hash[y].push_back((Data){val, pos});
}

int find1(LL val) {
    int y = (val + inf) % prime; //注意差值可能为负数,统一加上偏移量!
    int ret = n;
    int len = hash[y].size();
    for (int i = 0; i < len; i++)
        if (hash[y][i].value >= val) ret = min(ret, hash[y][i].info); //贪心思想:如果有多个数,找最前面的一个
    if (ret == n) return -1; else return ret; //编号为 0~n-1,若扫完一遍仍为 n 则说明找不到
}

int main(void) {
    freopen("1827.in", "r", stdin);
    freopen("1827.out", "w", stdout);
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%lld", &a[i]);
        insert1(a[i], i);
    }
    int ans = 1;
    for (int i = 0; i + 1 < n; i++) {
        for (int j = i + 1; j < n; j++) {
            LL c = a[j] - a[i]; //数列公差
            int sum = 2; //数列长度,至少有 a[i] 和 a[j] 这两项
            LL num = a[j] + c;
            int p = j; //迭代用
            int t = find1(num);
            while (t > p) {
                sum++;
                p = t;
                num += c;
                if (num>inf||num<0)break; //越界处理
                t = find1(num);
            }
            ans = max(ans, sum);
        }
    }

    printf("%d\n", ans);
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值