zoukankan      html  css  js  c++  java
  • 最大团题目

    hdu1530 Maximum Clique

    题意:给定一个无向图,求最大团数

    分析:求最大团,也就是求一个最大的完全子图,只能靠搜索了,用一个dp数组剪枝

    搜索思路倒是很清晰

    一般思路:

    枚举每一个点,假设选择了这个点,那么最大团组成的集合就可能是由与这个点关联的点的集合组成,接下来就是深搜选择这个集合里面的点的过程了。

    剪枝:

    1)重新排列了访问的顺序,按度数大的先访问

    2)用一个dp[]数组,dp[i]表示i到n-1范围内的点组成的最大团数,那么dp[i-1] 的最大可能值就是 dp[i]+1了,这个可以在搜索过程用来剪枝

    悲剧,怎么改都是1000+ms

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 55;
    int dp[N];//dp[i] 表示 [i,n) 范围内的最大团个数
    bool inset[N],g[N][N];
    int n,best,ord[N],deg[N];
    bool found;//记录每次搜索时是否更新
    void Memcpy(bool *d,bool *s)
    {
    for(int i=0;i<n;i++)
    d[i]=s[ord[i]];
    }
    bool cmp(int a,int b)
    {
    return deg[a]<deg[b];
    }
    int find(int start,bool *s)//查找第一个关联的点
    {
    for(int i=start+1;i<n;i++)
    if(s[i])
    return i;
    return -1;
    }
    void clique(bool *s,int start,int len)
    {
    int first=find(start,s),i;
    if(first==-1)
    {
    if(len>best)
    {
    best=len;
    found=true;
    }
    return ;
    }
    bool tmp[N];
    while(first!=-1)
    {
    if(len+n-start<=best || len+dp[first]<=best)
    //已选择的点的个数+剩余点的个数<=最大可能值
    //已选择的点的个数+后面的最大团数<=最大可能值
    return ;
    tmp[first]=false;
    for(i=first+1;i<n;i++)
    tmp[i]=s[i]&g[ord[first]][ord[i]];//i之后与当前集合重叠的点,只有重叠的点才又可能与已选择的点关联
    clique(tmp,first,len+1);
    if(found) return ;
    first=find(first,s);
    }
    }
    int main()
    {
    while(scanf("%d",&n)==1 && n)
    {
    memset(deg,0,sizeof(deg));
    for(int i=0;i<n;i++)
    {
    ord[i]=i;
    for(int j=0;j<n;j++)
    {
    scanf("%d",&g[i][j]);
    deg[i]+=g[i][j];
    }
    }
    sort(ord,ord+n,cmp);//按度数重新排列访问顺序
    best=0;
    dp[n-1]=1;
    for(int i=n-2;i>=0;i--)
    {
    Memcpy(inset,g[ord[i]]);
    found=false;
    clique(inset,i,1);
    dp[i]=best;
    }
    printf("%d\n",dp[0]);
    }
    return 0;
    }

     pku 1419 Graph Coloring

    题意:给定一个无向图,求一个最大点独立集。

    分析:首先由

    最大团数=补图的最大点独立集

    反之亦然,也就是补图的最大团数

    所以,还是直接用模板,只不过要记录一下路径

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 101;
    int dp[N];
    bool inset[N],g[N][N];
    int n,best,ord[N],deg[N];
    bool found;
    int set[N],ans[N];
    void Memcpy(bool *d,bool *s)
    {
    for(int i=0;i<n;i++)
    d[i]=s[ord[i]];
    }
    bool cmp(int a,int b)
    {
    return deg[a]<deg[b];
    }
    int find(int start,bool *s)
    {
    for(int i=start+1;i<n;i++)
    if(s[i])
    return i;
    return -1;
    }
    void clique(bool *s,int start,int len)
    {
    int first=find(start,s),i;
    if(first==-1)
    {
    if(len>best)
    {
    best=len;
    for(int i=0;i<best;i++)
    ans[i]=set[i];
    found=true;
    }
    return ;
    }
    bool tmp[N];
    while(first!=-1)
    {
    if(len+n-start<=best || len+dp[first]<=best)
    return ;
    tmp[first]=false;
    set[len]=ord[first];
    for(i=first+1;i<n;i++)
    tmp[i]=s[i]&g[ord[first]][ord[i]];
    clique(tmp,first,len+1);
    if(found) return ;
    first=find(first,s);
    }
    }
    int main()
    {
    int T,m,a,b;
    scanf("%d",&T);
    while(T--)
    {
    scanf("%d %d",&n,&m);
    memset(deg,0,sizeof(deg));
    memset(g,true,sizeof(g));
    for(int i=0;i<n;i++)
    {
    ord[i]=i;
    deg[i]=n;
    }
    while(m--)
    {
    scanf("%d %d",&a,&b);
    a--;b--;
    g[a][b]=false;
    g[b][a]=false;
    deg[a]--;
    deg[b]--;
    }
    sort(ord,ord+n,cmp);
    best=0;
    dp[n-1]=1;
    for(int i=n-2;i>=0;i--)
    {
    Memcpy(inset,g[ord[i]]);
    found=false;
    set[0]=ord[i];
    clique(inset,i,1);
    dp[i]=best;
    }
    sort(ans,ans+dp[0]);
    printf("%d\n%d",dp[0],ans[0]+1);
    for(int i=1;i<dp[0];i++)
    printf(" %d",ans[i]+1);
    printf("\n");
    }
    return 0;
    }

     pku1129  Channel Allocation

    还是模板题

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 30;
    int dp[N];
    bool inset[N],g[N][N];
    int n,best,ord[N],deg[N];
    bool found;
    void Memcpy(bool *d,bool *s)
    {
    for(int i=0;i<n;i++)
    d[i]=s[ord[i]];
    }
    bool cmp(int a,int b)
    {
    return deg[a]<deg[b];
    }
    int find(int start,bool *s)
    {
    for(int i=start+1;i<n;i++)
    if(s[i])
    return i;
    return -1;
    }
    void clique(bool *s,int start,int len)
    {
    int first=find(start,s),i;
    if(first==-1)
    {
    if(len>best)
    {
    best=len;
    found=true;
    }
    return ;
    }
    bool tmp[N];
    while(first!=-1)
    {
    if(len+n-start<=best || len+dp[first]<=best)
    return ;
    tmp[first]=false;
    for(i=first+1;i<n;i++)
    tmp[i]=s[i]&g[ord[first]][ord[i]];
    clique(tmp,first,len+1);
    if(found) return ;
    first=find(first,s);
    }
    }
    int main()
    {
    char str[50];
    while(scanf("%d",&n)==1 && n)
    {
    memset(g,false,sizeof(g));
    memset(deg,0,sizeof(deg));
    for(int i=0;i<n;i++)
    {
    ord[i]=i;
    scanf("%s",str);
    for(int j=2;j<strlen(str);j++)
    {
    int t=str[j]-'A';
    g[i][t]=true;
    g[t][i]=true;
    deg[i]++;
    deg[t]++;
    }
    }
    sort(ord,ord+n,cmp);
    best=0;
    dp[n-1]=1;
    for(int i=n-2;i>=0;i--)
    {
    Memcpy(inset,g[ord[i]]);
    found=false;
    clique(inset,i,1);
    dp[i]=best;
    }
    if(dp[0]==1)
    printf("%d channel needed.\n",dp[0]);
    else printf("%d channels needed.\n",dp[0]);
    }
    return 0;
    }

     hdu 1045 Fire Net

    题意:给定一个n*n的矩阵,'.'表示空白区,‘X’表示墙壁,求在该矩阵上放置碉堡的数量的最大值,使得所有碉堡不能相互攻击(墙壁可以挡住攻击)

    分析:将矩阵上的所有点重新标号,0~n*n,同时,将所有可以直接攻击到的点连接起来,这样,题目就转变成了求一个图的最大独立集。

    设墙壁的数量为x

    所以最后可以放置碉堡的数量=最大独立点数-x (最大独立点数里面包含了墙壁数)

    又因为最大独立数=补图的最大团数,所有就可以用最大团解决了。

    View Code
    #include<iostream>
    #include<algorithm>
    using namespace std;
    const int N = 20;
    char g[5][5];
    int dir[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
    int dp[N];
    bool inset[N],mat[N][N];
    int r,n,best,ord[N],deg[N],num;
    bool found;
    void Memcpy(bool *d,bool *s)
    {
    for(int i=0;i<n;i++)
    d[i]=s[ord[i]];
    }
    bool cmp(int a,int b)
    {
    return deg[a]<deg[b];
    }
    int find(int start,bool *s)
    {
    for(int i=start+1;i<n;i++)
    if(s[i])
    return i;
    return -1;
    }
    void clique(bool *s,int start,int len)
    {
    int first=find(start,s),i;
    if(first==-1)
    {
    if(len>best)
    {
    best=len;
    found=true;
    }
    return ;
    }
    bool tmp[N];
    while(first!=-1)
    {
    if(len+n-start<=best || len+dp[first]<=best)
    return ;
    tmp[first]=false;
    for(i=first+1;i<n;i++)
    tmp[i]=s[i]&mat[ord[first]][ord[i]];
    clique(tmp,first,len+1);
    if(found) return ;
    first=find(first,s);
    }
    }
    bool check(int x,int y)
    {
    if(x<0||x>=r || y<0 || y>=r)
    return false;
    return true;
    }
    void build()
    {
    num=0;
    memset(mat,true,sizeof(mat));
    for(int i=0;i<n;i++)
    {
    deg[i]=n;
    ord[i]=i;
    }
    for(int i=0;i<r;i++)
    {
    for(int j=0;j<r;j++)
    {
    int a=i*r+j,b;
    if(g[i][j]=='X'){
    num++;
    continue;
    }
    for(int k=0;k<4;k++)
    {
    int x=dir[k][0]+i;
    int y=dir[k][1]+j;
    while(check(x,y) && g[x][y]!='X')
    {
    b=x*r+y;
    mat[a][b]=false;
    deg[a]--;
    deg[b]--;
    x+=dir[k][0];
    y+=dir[k][1];
    }
    }
    }
    }
    }
    int main()
    {
    while(scanf("%d",&r)==1 && r)
    {
    for(int i=0;i<r;i++)
    scanf("%s",g[i]);
    n=r*r;
    build();
    sort(ord,ord+n,cmp);
    best=0;
    dp[n-1]=1;
    for(int i=n-2;i>=0;i--)
    {
    Memcpy(inset,mat[ord[i]]);
    found=false;
    clique(inset,i,1);
    dp[i]=best;
    }
    printf("%d\n",dp[0]-num);
    }
    return 0;
    }
  • 相关阅读:
    二叉排序树和平衡二叉树
    博客首页特效整理2
    博客首页特效整理
    19-20下学期思维导图
    19-20下学期第一次作业问卷调查回答
    c++实现五子棋游戏
    c++实现扫雷游戏
    c++实现2048游戏
    c++实现推箱子游戏
    C++实现贪吃蛇小游戏
  • 原文地址:https://www.cnblogs.com/nanke/p/2385989.html
Copyright © 2011-2022 走看看