枚举+剪枝 codeforces303C Minimum Modular

感觉这题的思想可以借鉴

如果k为0,题目会简单许多

建立一个数组S,S[i]表示原数组中,有多少对两式相减等于i

那么如何判断i是否满足题意呢,只要看

S[i],S[2*i],S[3*i]....一直到n*i大于原数组中的最大值,如果这些都满足,就说明i是成立的,就是答案了


然而现在有k这个东西,所以我们只能用最原始的那种方法去判断

用一个标记数组vis,vis[p]表示A[j]%i等于p出现的次数。那么我们就可以统计到底有多少个取模后重复出现的次数,就能判断是否符合答案了

但是这个复杂度实在是太高了,,联想我们之前的想法,能不能有什么剪枝呢?


继续上面的方法处理数组S,将S[i],S[2*i],S[3*i]..... 的值都加起来,记为 cnt

会有0<=cnt<=n*(n-1)/2  其中当n全是i的倍数的时候,才会出现这种极端情况

又因为,如果想满足题意,又有n-k<=1,即n<=k+1

所以有cnt<=cnt<=n*(n-1)/2<=(k+1)*k/2

那么,如果我们发现了有cnt>(k+1)*k/2,那么不可能会成为答案,直接剪枝


如果可能成为答案,我们再用最原始的方法去记录A[j]%i每个值出现的次数看重复的是否大于k即可


其中,还在codeforces上看到vis里面有个小技巧~

如果vis只用0和1,那么理论来讲,我在第一层for循环里面的开头,每次都需要清空vis数组

但是这个代码并没有这样做,第一层for循环的循环变量是i

我们每次本来是给vis赋值1,我们却赋值的是i,然后判断的时候是判断vis[j]==i是否成立

这样的话,每次循环i+1后,以前的vis里的值都不可能等于现在的i,所以相当于是0了~

感觉这种写法好神奇


题目里还有个陷阱,当n-k<=1时,直接输出1。因为此时可以把n删成只有一个数字,所以取模1也满足条件了


#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<vector>
#include<ctime>
#include<functional>
#include<algorithm>

using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int A[MX], S[MX], vis[MX];
int main() {
    int n, k, Max = 0;
    scanf("%d%d", &n, &k);

    for(int i = 1; i <= n; i++) {
        scanf("%d", &A[i]);
        Max = max(Max, A[i]);
    }

    for(int i = 1; i <= n; i++) {
        for(int j = i + 1; j <= n; j++) {
            S[abs(A[i] - A[j])]++;
        }
    }

    if(k >= n - 1) {
        printf("1\n");
        return 0;
    }

    int ans = 0;
    for(int i = n - k; i <= Max + 1; i++) {
        int cnt = 0, sign = false;
        for(int j = i; j <= Max + 1; j += i) {
            cnt += S[j];
            if(cnt > k * (k + 1) / 2) {
                sign = true;
                break;
            }
        }
        if(sign) continue;

        cnt = 0;
        sign = false;
        for(int j = 1; j <= n; j++) {
            if(vis[A[j] % i] != i) {
                vis[A[j] % i] = i;
            } else cnt++;
            if(cnt > k) {
                sign = true;
                break;
            }
        }
        if(sign) continue;

        ans = i;
        break;
    }

    printf("%d\n", ans);
    return 0;
}


你可能感兴趣的:(枚举+剪枝 codeforces303C Minimum Modular)