最大团解析及用处

                           最大团解析及用处

 

题目:

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

////

以下是转载他人的博客: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\n",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\n",ans);


       for(int i = 1;i <= ans;++i){
           printf("%d",mc.res[i] + 1);
           if(i == ans) printf("\n");
           else printf(" ");
       }
   }
    return 0;
}


 

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

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

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

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值