奶牛卧室

题目链接:http://acm.swust.edu.cn/oj/problem/91/

这道题让我想起了hash,题意可以描述为为 n 个数(a[1…n])寻找一个最小的数 K,使得这 n 个数对 K 取模无冲突。

(nleq5000, a[i]in[1, 1000000])

看了一个题解,A是A了,但是复杂度不是很好理解。并且这道题貌似有BUG,只对差作标记而不考虑差的约数的代码也通过了,显然可以构造出对应的样例:

输入:2 1 5

输出:3

目前对这个题解理解的并不是很清晰,还没有找到这道题的出处和更好的解法。

如果不考虑复杂度,暴力的方法将会是对每个差的大于n的约数标记,然后从n开始递增找第一个未被标记的数。

差的范围也是[1, 1000000],如果暴力遍历每个数的约数,那必定超时。

反过来考虑,遍历每个可能的约数:因为满足条件的约数在([n, max{{|a[i]-a[j]|}}])之间,这样所有可能的约数在1000000级别,如果考虑每个约数都对应着一个另一个因子,这个因子可以进行素因子分解,素因子的范围不超过(sqrt{1000000}=1000),1000以内的素数不超过200个,这样加上一些细节优化,就有可能通过时限(5s)了。

这里标记约数的方法很巧妙,见代码。

// http://acm.swust.edu.cn/oj/problem/91/
# include <stdio.h>
# include <stdlib.h>

# define INDEX(i) ((i)>>3)
# define OFF(i) ((i)&0x7)

# define GET(i) (ok[INDEX(i)]>>OFF(i) & 0x1)
# define SET(i) (ok[INDEX(i)] |= 0x1<<OFF(i))

int pt[200], pn = 0;

bool isPrime(int x) {
    if (x%2==0) return x==2;
    if (x%3==0) return x==3;
    if (x%5==0) return x==5;
    for (int i = 7; i*i <= x; i+=2) {
        if (x%i==0) return 0;
    }
    return 1;
}

int n;
int s[5005];
char ok[INDEX(1000001)+1];

int main()
{
    for (int i = 2; i < 1001; i+=2) {
        if (isPrime(i)) pt[pn++] = i;
    }
    
    scanf("%d", &n);
    for (int i = 0; i < n; ++i) scanf("%d", &s[i]);
    
    int mx = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = i+1; j < n; ++j) {
            int t = (int)abs(s[i]-s[j]);
            SET(t);
            if (t > mx) mx = t;
        }
    }
    for (int i = 0; i<pn && pt[i]*n<=mx; ++i) {
        int k = pt[i];
        while (mx >= n*k) {
            for (int j = n; j*k < mx; ++j) if (GET(j*k)) SET(j);
            k *= pt[i];
        }
    }
    for (int i = n; i <= mx+1; ++i) if (!GET(i)) {
        printf("%d
", i);
        break;
    }
    
    return 0;
}
原文地址:https://www.cnblogs.com/txd0u/p/3450361.html