• 最大团


    最大团

    1.算法分析

    概念: 当G′是图G的子图,且G′是关于V′的完全图时,子图G'为图G的团;当G'是团,且不是其他团的子集时,G'为图G的极大团;当G'是极大团时,且点数最多,G'为图G最大团

    一般无向图的最大独立集=补图的最大团
    求最大团使用Bron–Kerbosch算法,时间O(3^(n/3))
    基本思想就是dfs+剪枝

    2. 典型例题

    2.1 板子题

    ZOJ 1492 Maximum Clique

    #include <bits/stdc++.h>
    
    int const N = 100;
    int g[N][N], group[N], vis[N], cnt[N], n, ans;
    
    using namespace std;
    
    // u当前节点,pos当前最大团内的点数
    bool dfs( int u, int pos )
    {
        int i, j;
        for( i = u + 1; i <= n; i++)  // 遍历其他的点
        {
            if( cnt[i] + pos <= ans ) return 0;  // 可行性剪枝
            if( g[u][i] )  // 必须有连边
            {
                for( j = 0; j < pos; j++ ) if( !g[i][ vis[j] ] ) break; // 判断这个最大团内其他的点是否和i都有连边
                if( j == pos )  // 如果这个团内的点都和i点有连边
                {    
                    vis[pos] = i;  // 把i点放入最大团
                    if( dfs( i, pos+1 ) ) return 1;    // 判断以i为起点的最大团是否存在  
                }    
            }
        }    
    
        // 记录最大团内的所有点
        if( pos > ans )  // 如果比原先的最大团大
        {
            for( i = 0; i < pos; i++ )
                group[i] = vis[i]; 
            ans = pos;
            return 1;    
        }    
        return 0;
    } 
    
    // 求最大团
    int maxclique()
    {
        ans = -1;  // 记录这张图的最大团
        for (int i = n; i > 0; --i)  // 从n开始是为了保证先得到以点数大的点开始的最大团,使得后续好继承它的状态,利于剪枝
        {
            vis[0] = i;  // 一开始最大团内点为i
            dfs(i, 1);  // 从i点开始dfs
            cnt[i] = ans;  // 更新以i为起点的最大团内的点数
        }
        return ans;
    }
    
    int main()
    {
        while (cin >> n && n)
        {
            /* code */
            for (int i = 1; i <= n; ++i)
                for (int j = 1; j <= n; ++j)
                    cin >> g[i][j];
            
            cout << maxclique() << endl;
            
        }
        return 0;
    }
    

    2.2 二分+最大团

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

    /*
    二分答案后,如果两个点之间的距离大于当前的判断值,加边,在用最大团跑一下,
    根据得到最大团点的数量和 k 的大小关系,调整二分的上下界
    */
    #include <bits/stdc++.h>
    
    using namespace std;
    
    int const N = 100;
    int group[N], vis[N], cnt[N], n, ans, m;
    pair<double, double> point[N];
    double g[N][N];
    double eps = 1e-5;
    
    
    // u当前节点,pos当前最大团内的点数
    bool dfs( int u, int pos )
    {
        int i, j;
        for( i = u + 1; i <= n; i++)  // 遍历其他的点
        {
            if( cnt[i] + pos <= ans ) return 0;  // 可行性剪枝
            if( g[u][i] )  // 必须有连边
            {
                for( j = 0; j < pos; j++ ) if( !g[i][ vis[j] ] ) break; // 判断这个最大团内其他的点是否和i都有连边
                if( j == pos )  // 如果这个团内的点都和i点有连边
                {    
                    vis[pos] = i;  // 把i点放入最大团
                    if( dfs( i, pos+1 ) ) return 1;    // 判断以i为起点的最大团是否存在  
                }    
            }
        }    
    
        // 记录最大团内的所有点
        if( pos > ans )  // 如果比原先的最大团大
        {
            for( i = 0; i < pos; i++ )
                group[i] = vis[i]; 
            ans = pos;
            return 1;    
        }    
        return 0;
    } 
    
    // 求最大团
    int maxclique()
    {
        ans = -1;  // 记录这张图的最大团
        for (int i = n; i > 0; --i)  // 从n开始是为了保证先得到以点数大的点开始的最大团,使得后续好继承它的状态,利于剪枝
        {
            vis[0] = i;  // 一开始最大团内点为i
            dfs(i, 1);  // 从i点开始dfs
            cnt[i] = ans;  // 更新以i为起点的最大团内的点数
        }
        return ans;
    }
    
    double dis(pair<double, double> x, pair<double, double> y)
    {
        return (y.first - x.first) * (double)(y.first - x.first) + (y.second - x.second) * (double)(y.second - x.second);
    }
    
    void build(double limit)
    {
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                if (i == j) continue;
                double dist = sqrt(dis(point[i], point[j]));
                if (dist >= limit) g[i][j] = 1;
            }
        }
    }
    
    // 判断距离最小的边最大是多少
    bool check(double limit)
    {
        memset(g, 0, sizeof g);
        build(limit);  // 两点间距离大于limit才能建图
        int ans = maxclique(); // 最大团内点数
        if (ans >= m) return true;
        else return false;
    }
    
    int main()
    {
        while(cin >> n >> m)
        {
            for (int i = 1; i <= n; ++i)
            {
                cin >> point[i].first >> point[i].second;
            }
    
            // 二分答案
            double l = 0, r = 20000.0;
            while (r - l > eps)
            {
                double mid = (l + r) / 2;
                if (check(mid)) l = mid;  // 当前最大团内点格式大于等于m,限制值设置太小
                else r = mid;
            }
            printf("%.2lf
    ", l);
        }
        return 0;
    }
    

    2.3一般无向图最大独立集

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

    /*最大团点的数量=补图中最大独立集点的数量。建立补图,求最大团即可*/
    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cstdlib>
    
    int const N = 100;
    int g[N][N], group[N], vis[N], cnt[N], n, ans, t, m;
    
    using namespace std;
    
    // u当前节点,pos当前最大团内的点数
    bool dfs( int u, int pos )
    {
        int i, j;
        for( i = u + 1; i <= n; i++)  // 遍历其他的点
        {
            if( cnt[i] + pos <= ans ) return 0;  // 可行性剪枝
            if( g[u][i] )  // 必须有连边
            {
                for( j = 0; j < pos; j++ ) if( !g[i][ vis[j] ] ) break; // 判断这个最大团内其他的点是否和i都有连边
                if( j == pos )  // 如果这个团内的点都和i点有连边
                {    
                    vis[pos] = i;  // 把i点放入最大团
                    if( dfs( i, pos+1 ) ) return 1;    // 判断以i为起点的最大团是否存在  
                }    
            }
        }    
    
        // 记录最大团内的所有点
        if( pos > ans )  // 如果比原先的最大团大
        {
            for( i = 0; i < pos; i++ )
                group[i] = vis[i]; 
            ans = pos;
            return 1;    
        }    
        return 0;
    } 
    
    // 求最大团
    int maxclique()
    {
        ans = -1;  // 记录这张图的最大团
        for (int i = n; i > 0; --i)  // 从n开始是为了保证先得到以点数大的点开始的最大团,使得后续好继承它的状态,利于剪枝
        {
            vis[0] = i;  // 一开始最大团内点为i
            dfs(i, 1);  // 从i点开始dfs
            cnt[i] = ans;  // 更新以i为起点的最大团内的点数
        }
        return ans;
    }
    
    int main()
    {
        cin >> t;
        while (t--)
        {
            cin >> n >> m;
            memset(g, 1, sizeof g);  // 1认为是有边
            for (int i = 1, a, b; i <= m; ++i)
            {
                scanf("%d %d", &a, &b);
                g[a][b] = g[b][a] = 0;  // 建立补图
            }
            
            cout << maxclique() << endl;
            for (int i = 0; i < ans; ++i) printf("%d ", group[i]);
            cout << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    Spring入门
    排序算法【整理】
    C#并发解决(lock)
    Java poi导出word表格
    layui table checkbox默认选中
    Element table表尾合计行嵌入input
    Java接收带List的实体类
    Web SQL Database+mui上传视频
    Web SQL Database+mui上传图片
    mui 上传视频
  • 原文地址:https://www.cnblogs.com/spciay/p/13128713.html
Copyright © 2020-2023  润新知