• 最大团解析及用处


                               最大团解析及用处

     

    题目:

       求一张图中。最多的点集的最大个数。称为最大团。

    ////////////////////////////////////////////////

    下面是转载他人的博客:JMJST

        从一个点 u 開始。把这个点增加集合 U 中。

    将编号比它大的且和它相连的点增加集合 S1 中,为了方便,将集合 S1 中的点有序,让他们从小到大排列。进行第一遍 DFS

        第一遍 DFS :

          从 S1 中选择一个点 u1,遍历 S1 中,全部编号比 u1 大且和 u1 相连的点,事实上也就是排在 u1 后面。而且和 u1 相连的点,将它们增加集合 S2 中。同理,让 S2 中的点也依照编号也从小到大排列。将 u1 增加集合 U 中,进行第二遍 DFS

        第二遍 DFS :

          从 S2 中选择一个点 u2。遍历 S2 中,全部排在 u2 后面且和 u2 相连的点,并把它们增加集合 S3 中,让 S3 中的点依照编号从小到大排列。将 u2 增加集合 U 中进行第三遍 DFS

        第三遍 DFS :

          从 S3 中选择一个点 u3,遍历 S3 中,全部排在 u3 后面且和 u3 相连的点,并把它们增加集合 S4 中,让 S4 中的点依照编号从小到大排列,将 u3 增加集合 U 中进行第四遍 DFS

        ......

        最底层的 DFS :

          当某个 S 集合为空时。DFS 过程结束,得到一个仅仅用后面几个点构成的全然子图,并用它去更新仅仅用后面几个点构成的最大团。

    退出当前 DFS。返回上层 DFS,接着找下一个全然子图。直到找全然部的全然子图

     

    上面的 DFS 过程,假设不加不论什么剪枝的话。事实上和第一个 DFS 是几乎相同的,可是既然我们都这样 DFS 了,能不能想一想怎么剪枝呢?

    如果我们当前处于第 i 层 DFS,如今须要从 Si 中选择一个 ui。把在 Si 集合中排在 ui 后面的和 ui 相连的点增加集合 S(i+1) 中,把 ui 加到集合 U 中

    可能大家稍作思考之后就想到了一个剪枝:

    剪枝1:假设 U 集合中的点的数量+1(选择 ui 增加 U 集合中)+Si 中全部 ui 后面的点的数量  当前最优值,不用再 DFS 了

    还有什么剪枝呢?

    注意到我们是从后往前选择 u 的,也就是说。我们在 DFS 初始化的时候。如果选择的是编号为 x 的点,那么我们肯定已经知道了用 [x+1, n] 。[x+2, n],[x+3, n] ...[n,n] 这些区间中的点能构成的最大团的数量是多大

    剪枝2:假设 U 集合中的点的数量+1(理由同上)+[ui, n]这个区间中能构成的最大团的顶点数量  当前最优值,不用再 DFS了

    有这两个剪枝就够了吗?

    不,我们还能想出一个剪枝来:

    剪枝3:假设 DFS 到最底层,我们可以更新答案,不用再 DFS 了。结束整个 DFS 过程,也不再返回上一层继续 DFS 了

    为什么?由于我们假设再继续往后 DFS 的话,点的编号变大了。可用的点变少了(可用的点在一開始 DFS 初始化的时候就确定了,随着不断的加深 DFS 的层数,可用的点在不断的降低)

    有了上面三个剪枝,100 个点以内的图,我们也能很快的出解了

    可能有人会问,假设想知道最大团包括哪些节点该怎么办?

    这还不简单?每次 DFS 都会加一个点进入 U 集合中,DFS 到最底层,更新最大团数量的时候,U 集合中的点一定是一个全然子图中的点集,用 U 集合更新最大团的点集即可了

    经常使用结论:

    1、最大团点的数量=补图中最大独立集点的数量

    2、二分图中,最大独立集点的数量+最小覆盖点的数量=整个图点的数量

    3、二分图中,最小覆盖点的数量=最大匹配的数量

    4、图的染色问题中,最少须要的颜色的数量=最大团点的数量

    1、先来一道裸题:ZOJ 1492 Maximum Clique

     

    /*
       题目形式:最大团
    
       给了一个最多包括 50 个点的无向图,
       让求这个图中最大团所包括的的点的数量
    
    */
    
    const int MAXN = 60;
    
    class maxClique{
        public:
           static const int N = 60;   //数据范围
           bool DFS(int cur,int tot);
           int maxclique();
            bool G[N][N];
            int n,Max[N],Alt[N][N],ans;
            //Max:当前集合最大点数,Alt:集合
    };
    
    bool maxClique::DFS(int cur,int tot){
        if(cur == 0){
            if(tot > ans){
                ans = tot;
                return true;
            }
            return false;
        }
        for(int i = 0;i < cur;++i){
            if(cur - i + tot <= ans) //剪枝1
                return false;
            int u = Alt[tot][i];
            if(Max[u] + tot <= ans) // 剪枝2
                return false;
            int next = 0;
            for(int j = i + 1;j < cur;++j)
                if(G[u][Alt[tot][j]])
                   Alt[tot+1][next++] = Alt[tot][j];
            if(DFS(next,tot+1))
                return true;
        }
        return false;
    }
    
    
    int maxClique::maxclique(){
        ans = 0;
        memset(Max,0,sizeof(Max));
        for(int i = n - 1;i >= 0;--i){
            int cur = 0;
            for(int j = i + 1;j < n;++j)
                if(G[i][j]) Alt[1][cur++] = j;
            DFS(cur,1);
            Max[i] = ans;
        }
        return ans;
    }


     

    来一个略微麻烦点的题:HDU 3585 maximum shortest distance

    给了平面上 n 个点。要求选出 k 个点来,使得这 k 个点中,距离近期的两个点的距离最大。n 最大为50

    二分答案后。假设两个点之间的距离大于当前的推断值,加边。在用最大团跑一下。依据得到最大团点的数量和 k 的大小关系。调整二分的上下界/。

       一開始二分搜索次数太大了,结果超时了!!这个教训一定要记住啊!!。!

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    /*
       题目形式:最大团
    
       给了一个最多包括 50 个点的无向图,
       让求这个图中最大团所包括的的点的数量
    
    */
    
    const int MAXN = 60;
    
    class maxClique{
        public:
           static const int N = 60;   //数据范围
           bool DFS(int cur,int tot);
           int maxclique();
            bool G[N][N];
            int n,Max[N],Alt[N][N],ans;
            //Max:当前集合最大点数。Alt:集合
    };
    
    bool maxClique::DFS(int cur,int tot){
        if(cur == 0){
            if(tot > ans){
                ans = tot;
                return true;
            }
            return false;
        }
        for(int i = 0;i < cur;++i){
            if(cur - i + tot <= ans) //剪枝1
                return false;
            int u = Alt[tot][i];
            if(Max[u] + tot <= ans) // 剪枝2
                return false;
            int next = 0;
            for(int j = i + 1;j < cur;++j)
                if(G[u][Alt[tot][j]])
                   Alt[tot+1][next++] = Alt[tot][j];
            if(DFS(next,tot+1))
                return true;
        }
        return false;
    }
    
    
    int maxClique::maxclique(){
        ans = 0;
        memset(Max,0,sizeof(Max));
        for(int i = n - 1;i >= 0;--i){
            int cur = 0;
            for(int j = i + 1;j < n;++j)
                if(G[i][j]) Alt[1][cur++] = j;
            DFS(cur,1);
            Max[i] = ans;
        }
        return ans;
    }
    
    struct Point{
        double x,y;
    
        double dist(const Point& a){
             return(sqrt((x - a.x)*(x - a.x) + (y - a.y)*(y - a.y)));
        }
    }A[MAXN];
    
    int N,K;
    maxClique mc;
    
    void build(double R){
        mc.n = N;
        for(int i = 0;i < mc.n;++i){
            for(int j = 0;j < mc.n;++j){
                if(A[i].dist(A[j]) >= R) mc.G[i][j] = 1;
                else mc.G[i][j] = 0;
            }
        }
    }
    
    int main()
    {
        //freopen("Input.txt","r",stdin);
    
        while(~scanf("%d%d",&N,&K)){
            for(int i = 0;i < N;++i){
                scanf("%lf%lf",&A[i].x,&A[i].y);
            }
    
            double lb = 0,ub = 20000;
            for(int k = 0;k < 40;++k){
                double mid = (lb + ub) / 2;
                build(mid);
                if(mc.maxclique() >= K)
                    lb = mid;
                else
                    ub = mid;
            }
            printf("%.2lf
    ",lb);
        }
        return 0;
    }
    

    3、来一个一般无向图最大独立集的题目:POJ 1419 Graph Coloring

    给了一个有 n 个点 m 条边的无向图,要求用黑、白两种色给图中顶点涂色,相邻的两个顶点不能涂成黑色,求最多能有多少顶点涂成黑色。图中最多有 100 个点

    利用上面提到的结论:最大团点的数量=补图中最大独立集点的数量。建立补图,求最大团就可以

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    /*
       题目形式:最大团
    
       给了一个最多包括 50 个点的无向图。
       让求这个图中最大团所包括的的点的数量
    
    */
    
    const int MAXN = 60;
    
    class maxClique{
        public:
           static const int N = 106;   //数据范围
           bool DFS(int cur,int tot);
           int maxclique();
            bool G[N][N];
            int Max[N],Alt[N][N];
            int x[N],y[N];
            int n,ans,*path,*res;
            //Max:当前集合最大点数,Alt:集合
    };
    
    bool maxClique::DFS(int cur,int tot){
        if(cur == 0){
            if(tot > ans){
                swap(path,res);
                ans = tot;
                return true;
            }
            return false;
        }
        for(int i = 0;i < cur;++i){
            if(cur - i + tot <= ans) //剪枝1
                return false;
            int u = Alt[tot][i];
            if(Max[u] + tot <= ans) // 剪枝2
                return false;
            int next = 0;
            for(int j = i + 1;j < cur;++j)
                if(G[u][Alt[tot][j]])
                   Alt[tot+1][next++] = Alt[tot][j];
            path[tot+1] = u;
            if(DFS(next,tot+1))
                return true;
        }
        return false;
    }
    
    
    int maxClique::maxclique(){
        ans = 0;
        memset(Max,0,sizeof(Max));
        path = x; res = y;
        for(int i = n - 1;i >= 0;--i){
            int cur = 0;
            path[1] = i;   //起点
            for(int j = i + 1;j < n;++j)
                if(G[i][j]) Alt[1][cur++] = j;
            DFS(cur,1);
            Max[i] = ans;
        }
        return ans;
    }
    
    int N,K;
    maxClique mc;
    
    
    
    int main()
    {
       // freopen("Input.txt","r",stdin);
    
       int T,m;
       scanf("%d",&T);
       for(int kase = 1;kase <= T;++kase){
           scanf("%d%d",&mc.n,&m);
           memset(mc.G,true,sizeof(mc.G));
    
           for(int i = 0,a,b;i < m;++i){
               scanf("%d%d",&a,&b);
               mc.G[a - 1][b - 1] = mc.G[b - 1][a - 1] = 0;
           }
           int ans = mc.maxclique();
           printf("%d
    ",ans);
    
    
           for(int i = 1;i <= ans;++i){
               printf("%d",mc.res[i] + 1);
               if(i == ans) printf("
    ");
               else printf(" ");
           }
       }
        return 0;
    }
    


     

    4、来一个染色问题:POJ 1129 Channel Allocation

    最多 26 广播电台...我还是讲抽象之后的题意吧:最多26个点的无向图,要求相邻的节点不能染成同一个颜色。问最少须要多少颜色染全然部的顶点

    利用上面提到的结论:图的染色问题中。最少须要的颜色的数量=最大团点的数量,建图,跑最大团就可以,另外。这题还须要构造解

  • 相关阅读:
    线性回归学习历程
    CART决策树的学习历程
    markdown测试
    开张大吉+代码测试
    使用tomcat启动dubbo项目
    ThreadLocal 工作原理、部分源码分析
    Dubbo项目demo搭建
    redis 操作 list 的测试
    redis 操作 hash 的测试
    redis 操作string 的测试
  • 原文地址:https://www.cnblogs.com/wzzkaifa/p/6945307.html
Copyright © 2020-2023  润新知