最大团(最大完全子图)

完全图:

任意两点都恰有一条边相连的图(任意两点都相邻)

完全子图:

满足任意两点都恰有一条边相连的子图,也叫团

最大团:

就是最大完全子图(最大指的是点数最多)

常用结论:

1、最大团点的数量=补图中最大独立集点的数量
2、二分图中,最大独立集点的数量+最小覆盖点的数量=整个图点的数量
3、二分图中,最小覆盖点的数量=最大匹配的数量
4、图的染色问题中,最少需要的颜色的数量=最大团点的数量

ps:
最大团问题是NP问题


hdu1530 Maximum Clique

题意:

求最大团的点数
裸题

code1:

2000-ms

#include
#include
using namespace std;
const int maxm=55;
int g[maxm][maxm];//存图
int vis[maxm];//存放已经选择的点
int cnt[maxm];//cnt[i]表示用编号>=i的点能组成的最大团的点数
int ans;//答案
int n;//顶点数
bool dfs(int cur,int num){//num是已经选择的点数
    for(int i=cur+1;i<=n;i++){//枚举所有编号比cur大的点作为下一个选择的点
        if(cnt[i]+num<=ans){//不可能更新答案了
            return 0;//剪枝
        }
        if(g[cur][i]){//如果和i相邻
            int ok=1;//flag
            for(int j=0;j<num;j++){//判断是否和所有已选中的点相邻
                if(!g[i][vis[j]]){//如果有一个点不相邻则不行
                    ok=0;
                    break;
                }
            }
            if(ok){//如果i和所有已经选中的点相邻
                vis[num]=i;
                if(dfs(i,num+1)){//第一次dfs成功的一定是最大的
                    return 1;
                }
            }
        }
    }
    if(num>ans){//更新答案
        ans=num;
        return 1;
    }
    return 0;
}
void maxclique(){
    ans=0;
    for(int i=n;i>=1;i--){//从后往前枚举第一个选择的点
        vis[0]=i;
        dfs(i,1);
        cnt[i]=ans;
    }
    printf("%d\n",cnt[1]);
}
int main(){
    while(scanf("%d",&n)){
        if(n==0)break;
        for(int i=1;i<=n;i++){//输入图
            for(int j=1;j<=n;j++){
                scanf("%d",&g[i][j]);
            }
        }
        maxclique();
    }
    return 0;
}
code2:

1500-ms,还没完全看懂先挂着

#include 
#include 
using namespace std;
const int N=60;
int G[N][N];
int n,Max[N],Alt[N][N],ans;
bool DFS(int cur,int tot){
    if(cur==0) {
        if(tot>ans) {
            ans=tot;
            return 1;
        }
        return 0;
    }
    for(int i=0;i<cur;i++) {
        if(cur-i+tot<=ans)return 0;
        int u=Alt[tot][i];
        if(Max[u]+tot<=ans)return 0;
        int nxt=0;
        for(int j=i+1;j<cur;j++){
            if(G[u][Alt[tot][j]]){
                Alt[tot+1][nxt++]=Alt[tot][j];
            }
        }
        if(DFS(nxt,tot+1)){
            return 1;
        }
    }
    return 0;
}
int 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;
}
int main(){
    while(scanf("%d",&n),n) {
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                scanf("%d",&G[i][j]);
            }
        }
        printf("%d\n",MaxClique());
    }
    return 0;
}
//来源:https://www.cnblogs.com/zhj5chengfeng/p/3224092.html

poj1419 Graph Coloring

题意:

把给定的图用黑色和白色染色,要求黑色不能和黑色相邻且黑色的点尽可能多,
输出染色之后的黑色点数量以及黑色的点的编号(即输出方案)

思路:

黑色不能相邻且要求黑色点最多,即求最大独立集
最大独立集点数=补图的最大团点数
建补图然后求最大团就行了

题目要求输出方案,只要在每次更新最大团的时候记录方案就行了

code:
#include
#include
#include
using namespace std;
const int maxm=105;
int g[maxm][maxm];
int group[maxm];//存最大团方案
int cnt[maxm];
int vis[maxm];
int n,m;
int ans;
bool dfs(int cur,int num){
    for(int i=cur+1;i<=n;i++){
        if(cnt[i]+num<=ans)return 0;
        if(g[cur][i]){
            int ok=1;
            for(int j=0;j<num;j++){
                if(!g[i][vis[j]]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                vis[num]=i;
                if(dfs(i,num+1)){
                    return 1;
                }
            }
        }
    }
    if(num>ans){
        ans=num;
        for(int i=0;i<num;i++){//记录方案
            group[i]=vis[i];
        }
        return 1;
    }
    return 0;
}
void maxclique(){
    ans=0;
    for(int i=n;i>=1;i--){
        vis[0]=i;
        dfs(i,1);
        cnt[i]=ans;
    }
}
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                g[i][j]=1;//默认相连,把输入的边删掉就是补图
            }
        }
        for(int i=1;i<=m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            g[a][b]=g[b][a]=0;//删边
        }
        maxclique();
        printf("%d\n",ans);//输出cnt[1]也行
        for(int i=0;i<ans-1;i++){
            printf("%d ",group[i]);
        }
        printf("%d\n",group[ans-1]);
    }
    return 0;
}

poj1129 Channel Allocation

题意:

染色题,要求相邻的点颜色不同,问最少要用多少种颜色

思路:

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

显然最大团中的点必须两两颜色不同

还可以用反证法证明:如果x+1个点必须染成不同的颜色,那么这x+1个点必须相互连接,而x+1个点相互连接与最大团点数为x矛盾

code:
#include
#include
#include
using namespace std;
const int maxm=30;
int g[maxm][maxm];
int cnt[maxm];
int vis[maxm];
int n,m;
int ans;
bool dfs(int cur,int num){
    for(int i=cur+1;i<=n;i++){
        if(cnt[i]+num<=ans)return 0;
        if(g[cur][i]){
            int ok=1;
            for(int j=0;j<num;j++){
                if(!g[i][vis[j]]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                vis[num]=i;
                if(dfs(i,num+1)){
                    return 1;
                }
            }
        }
    }
    if(num>ans){
        ans=num;
        return 1;
    }
    return 0;
}
void maxclique(){
    ans=0;
    for(int i=n;i>=1;i--){
        vis[0]=i;
        dfs(i,1);
        cnt[i]=ans;
    }
}
int main(){
    while(scanf("%d",&n)){
        if(n==0)break;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                g[i][j]=0;
            }
        }
        for(int i=1;i<=n;i++){
            char s[30];
            scanf("%s",s);
            int len=strlen(s);
            int a=s[0]-'A'+1;
            for(int j=2;j<len;j++){
                int b=s[j]-'A'+1;
                g[a][b]=g[b][a]=1;
            }
        }
        maxclique();
        if(ans==1){
            printf("1 channel needed.\n");
        }else{
            printf("%d channels needed.\n",ans);
        }
    }
    return 0;
}

hdu3585 maximum shortest distance

题意:

给n个点,和这n个点在坐标系上的坐标
现在要选出k个点,使得这k个点中距离最近的两个点的距离最大

思路:

最大团+二分

因为是坐标系上的点的距离,所以可以看作任意两点之间都有边
即可以选出的点组成的图可以看作是完全图

开结构体,存下任意两点的的距离和两点的编号,然后排序
因为答案肯定是这些距离中的某一个,所以二分最小距离的下标
把大于这个距离的边加入图中,这时候图中的最短距离就小于dis[mid]

但是题目还有一个要求是需要k个点,如何判断是否是k个点呢?
因为可以看作是完全图,所以整个图就是一个团,最大团的点数就是图的点数,

对于每次建立的新图,跑一遍最大团
如果最大团>=k说明至少有k个点,不断二分得出答案

ps:
我直接二分答案tle,所以二分下标
因为下标是int而且数组不是很大,比较稳

code:
#include
#include
#include
#include
#include
using namespace std;
const int maxm=55;
int g[maxm][maxm];
int cnt[maxm];
int vis[maxm];
int n,k;
int ans;
bool dfs(int cur,int num){
    for(int i=cur+1;i<=n;i++){
        if(cnt[i]+num<=ans)return 0;
        if(g[cur][i]){
            int ok=1;
            for(int j=0;j<num;j++){
                if(!g[i][vis[j]]){
                    ok=0;
                    break;
                }
            }
            if(ok){
                vis[num]=i;
                if(dfs(i,num+1)){
                    return 1;
                }
            }
        }
    }
    if(num>ans){
        ans=num;
        return 1;
    }
    return 0;
}
void maxclique(){
    ans=0;
    for(int i=n;i>=1;i--){
        vis[0]=i;
        dfs(i,1);
        cnt[i]=ans;
    }
}
int x[maxm],y[maxm];
struct Node{
    int a,b;
    double c;
    Node(){};
    Node(int aa,int bb){
        a=aa;
        b=bb;
        double xx=x[aa]-x[bb];
        double yy=y[aa]-y[bb];
        c=sqrt(xx*xx+yy*yy);
    }
    bool operator<(const Node a){
        return c<a.c;
    }
}e[maxm*maxm];
int main(){
    while(scanf("%d%d",&n,&k)!=EOF){
        for(int i=1;i<=n;i++){
            scanf("%d%d",&x[i],&y[i]);
        }
        int num=0;
        for(int i=1;i<=n;i++){
            for(int j=i+1;j<=n;j++){
                e[++num]=Node(i,j);
            }
        }
        sort(e+1,e+1+num);
        int l=1,r=num;
        int res=1;
        while(l<=r){//枚举答案的下标
            int mid=(l+r)/2;
            for(int i=1;i<=n;i++){
                for(int j=1;j<=n;j++){
                    g[i][j]=0;
                }
            }
            for(int i=mid;i<=num;i++){
                int a=e[i].a;
                int b=e[i].b;
                g[a][b]=g[b][a]=1;
            }
            maxclique();
            if(ans>=k){
                res=mid;
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        printf("%.2f\n",e[res].c);
    }
    return 0;
}

你可能感兴趣的:(图论,模板)