zoukankan      html  css  js  c++  java
  • bzoj 2571: Getting Rid of the Holidays

    Description

    B国的国王Johnny在他在位的短短几年里制定了不少的节日(事实上没超过30个),这些节日是为了尊敬各种各样他所想到的东西而设立的。每过一段固定的时间,一个节日将会被举行(即节日都是周期性的,每个节日的周期可能会不同),并且过节的那一天会普天同庆,将有盛会演奏、歌舞表演。有时候几个节日会在一天同时发生,并且有可能所有节日在同一天发生。如果出现这种情况,节日将被联合庆祝并且举行更大宴会。某一天所有节日都发生了。可能由于玩得太high了,在宴会后,国王Johnny表现开始有点古怪而且他需要与世隔绝几天。 
    在国王Johnny不在的时候(约48小时)你被指定处理B国的政事。最为一个真正的爱国者,你知道这些节日是对人民没有好处的,并且你想在国王Johnny回来之前(Johnny将不会介意,因为他在宴会后会忘记所有事情)删去一些节日。然而悲哀的是,这里的人民不知道什么对他们有好处,什么对他们有坏处,如果你删去了超过k个节日他们将会反抗。你的任务是删除不超过k个节日使得最小的两次有节日的日子之间的间隔最大。 

    Input

    文件的第一行有一个单独的整数T<=200,表示测试数据的组数。接下来有T组测试数据。对于每个测试数据,第一行包含两个整数n和K 
    (1 < = K < n < = 30 ),表示节日的总数和最多可以删除的节日数。接下来的一行包含n个整数表示每个节日的周期,第i个整数表示编号为i的整数的周期,每个节日的周期将不会超过10^18。 

    Output

    对于每个测试数据,输出一行一个数,表示最大的间隔.

    二分答案,两个节日可以共存等价于周期的gcd>=答案,可以用最大独立集判定

    这里给出的是的基于记忆化搜索的实现,时间复杂度上界为每组数据$O((sqrt{2})^{n}nlogn)$,适当剪枝可以通过此题

    #include<bits/stdc++.h>
    typedef long long i64;
    const int M=1<<16|111;
    int T,n,k,vp;
    i64 a[33],gs[33][33],vs[1007],lim;
    i64 gcd(i64 a,i64 b){return b?gcd(b,a%b):a;}
    int e[33],f[M],ed[M],tk=0;
    void maxs(int&a,int b){if(a<b)a=b;}
    int dp(int S,int w){
        if(S<M&&ed[S]==tk)return f[S];
        while(~S>>w&1)--w;
        int v=dp(S&~e[w],w-1)+1;
        if(v<n-k)maxs(v,dp(S^1<<w,w-1));
        if(S<M)ed[S]=tk,f[S]=v;
        return v;
    }
    bool chk(){
        int S=0;
        for(int i=0;i<n;++i){
            e[i]=0;
            for(int j=0;j<n;++j)if(gs[i][j]<lim)e[i]|=1<<j;
            S|=~e[i]&1<<i;
            e[i]|=1<<i;
        }
        ed[0]=++tk,f[0]=0;
        return dp(S,n-1)>=n-k;
    }
    int main(){
        for(scanf("%d",&T);T;--T){
            scanf("%d%d",&n,&k);
            for(int i=0;i<n;++i)scanf("%lld",a+i);
            std::random_shuffle(a,a+n);
            vp=0;
            for(int i=0;i<n;++i)for(int j=0;j<=i;++j)vs[vp++]=gs[j][i]=gs[i][j]=gcd(a[i],a[j]);
            std::sort(vs,vs+vp);
            vp=std::unique(vs,vs+vp)-vs;
            int L=0,R=vp-1,M;
            while(L<R){
                M=L+R+1>>1;
                lim=vs[M];
                if(chk())L=M;
                else R=M-1;
            }
            printf("%lld
    ",vs[L]);
        }
        return 0;
    }
  • 相关阅读:
    Android 编程下 Eclipse 恢复被删除的文件
    Android 编程下背景图片适配工具类
    Android 编程下 Managing Your App's Memory
    Android 编程下代码之(QQ消息列表滑动删除)
    Android 编程下 Canvas and Drawables
    Android 编程下 AlarmManager
    Android 编程下去除 ListView 上下边界蓝色或黄色阴影
    Java 编程下字符串的 16 位、32位 MD5 加密
    C#枚举类型和int类型相互转换
    MVC和普通三层架构的区别
  • 原文地址:https://www.cnblogs.com/ccz181078/p/7245013.html
Copyright © 2011-2022 走看看