最大团 HDU-1530

 传送门: 洛谷  Vjudge    (题目略有不同)

题目描述
  • 给定一个图 tt = (V, E)
  • 求一个点集 S ,使得对于任意 x ≠ y ∈ S ,x 和 y 都有一条边
  • |V | ≤ 50
输入格式
  第一行两个数,n, m 分别表示图的点数、边数。 接下
  来 m 行,每行两个整数 x, y 表示一条边 x ↔ y 。
输出格式
  输出最大团的大小以及最大团的数目。
样例输入
  4 5
  1 2
  2 3
  3 1
  1 4
  2 4
样例输出
  3 2

1. 搜索  2BornKerbosch算法 3.取反图求最大独立集 meet in the middle

1.  DFS 

  就是从每一个点出发找最大团

  从u点出发 枚举和u相连的点 check一下它是不是和现在找到的团一起构成完全图 

  如果是 就继续往下搜 不是 就继续找下一个点

  最重要的是找相连的点的顺序!!!

  这一步处理不好的话 既会找到重复的点 而且循环也是没完没了

  这里的方法是 只要找标号比 u 大的与之相连的点即可

  这样就有了一定的顺序性 而且也不会遗漏 

  因为假如最大的完全图中 有 u 还有比 u 小的点 v 

  即使在搜 u 时搜不到这个图 但是搜此图中标号最小的点时是一定搜得到的

  但是 这仍然不是最优秀的搜索 还有别的剪枝

    f [i] :从第 i , i+1, i+2 ,…,n 点中选出的最大团所含的点数 

    nw为现在选到的点 st为现在已经选出的点数+1 

    ans1 为最大团点数 ans2 为最大团的数量

  所谓"正难则反" 我们从 f [n] 求到 f[1]

  发现 f[i] 最大也只能为 f[i-1]+1

  所以 if ( st + f [nw+1] <ans1) 就直接 return 了 (最优性剪枝)

  However  这样子只能过掉HDU-1530 洛谷还是TLE

 

小结一下:    

    搜索的顺序可以解决很多问题啊 

    但是不要乱了 可以把问题一一列举出来 

    每想到一个方案的时候仔细想是不是可以解决这些问题

    想清楚 不要轻易否决了

    多想想要求的量之间的关系 有可能可以用于优化

 1 #include<iostream>
 2 #include<cstdio>
 3 #define go(i,a,b) for(register int i=a;i<=b;i++)
 4 #define yes(i,a,b) for(register int i=a;i>=b;i--)
 5 #define fre(x) freopen("x.in","r",stdin) freopen("x.out","w",stdout)
 6 using namespace std;
 7 int read()
 8 {
 9     int x=0,y=1;char c=getchar();
10     while(c<'0'||c>'9') {if(c=='-') y=-1;c=getchar();}
11     while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
12     return x*y;
13 }
14 int n,m,x,y,ans1,ans2;
15 int tmp[100],f[100];
16 bool mp[100][100];
17 bool ck(int x,int y)
18 {
19     go(i,1,y) {if(!mp[x][tmp[i]]) return 0;}
20     return 1;
21 }
22 int dfs(int nw,int st)
23 {
24     if(st+f[nw+1]<ans1) return ans1; 
25     if(st>ans1) {ans1=st;ans2=1;return ans1;}
26     else if(st==ans1) ans2++;
27     tmp[st]=nw;
28     go(i,nw+1,n) {if(ck(i,st)) dfs(i,st+1);}
29     return ans1;
30 }
31 int main()
32 {
33     //fre(1);
34     n=read();m=read();
35     go(i,1,m) {x=read();y=read();mp[x][y]=mp[y][x]=1;}
36     yes(i,n,1) {f[i]=dfs(i,1);}
37     printf("%d %d",ans1,ans2);
38     return 0;
39 }
40 
41 法1 洛谷 90pts View Code
法1 洛谷 90pts Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #define go(i,a,b) for(register int i=a;i<=b;i++)
 4 using namespace std;
 5 int read()
 6 {
 7     int x=0,y=1;char c=getchar();
 8     while(c<'0'||c>'9') {if(c=='-') y=-1;c=getchar();}
 9     while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
10     return x*y;
11 }
12 int n,m,x,y,ans1,ans2;
13 int tmp[100];
14 bool mp[100][100];
15 bool ck(int x,int y)
16 {
17     go(i,1,y) {if(!mp[x][tmp[i]]) return 0;}
18     return 1;
19 }
20 void dfs(int nw,int st)
21 {
22     if(st>ans1) {ans1=st;ans2=1;}
23     else if(st==ans1) ans2++;
24     tmp[st]=nw;
25     go(i,nw+1,n) {if(ck(i,st)) dfs(i,st+1);}
26 }
27 int main()
28 {
29     while(n=read())
30    {
31        if(!n) break ;
32        ans1=ans2=0;
33        go(i,1,n) go(j,1,n) mp[i][j]=0;
34        go(i,1,n) go(j,1,n) mp[i][j]=read();
35        go(i,1,n) dfs(i,1);
36        printf("%d\n",ans1); 
37    }
38     return 0;
39 }
法1 HDU AC Code

 

2. BornKerbosch算法 

 

  构造两个数组 P R 

  R 中存进入当前搜索的团中的点

  P 中存可能进入R中的点

  初始化 P里为所有元素 R 为空

  依次把 P 里的数丢进 R里

  我们现在把 v 放入 R 中 则要把 P 中不与 v 相连的点全都去掉 然后重复此步骤

        递归实现

  回溯时 R P 三个数组也要回溯成原来的样子

  为了方便 这三个数组可以设两个维度

  第一个维度表示现在所在的层数 第二个维度存元素

  还有一个优化 在同一层中 取了 v 那么就不必要取和 v 相连的点了 

        我的理解是这样的:

    假如 u 与 v 相连 我们取了 v 后不必要取 u 了

     u 所在的最大团有两种情况 : 1. 包括 v 那么取 v 时一定可以找到这个团

                 2. 不包括v 那么团里一定有别的点不与 v 相连

                  那么取那个点是一定可以找到这个团

    综上,没有取 u 的必要

 1 //BornKerbosch算法
 2 #include<bits/stdc++.h>
 3 using namespace std;
 4 #define RI register int
 5 #define LL long long
 6 inline int read()
 7 {
 8     int res=0,f=1;
 9     char ch=getchar();
10     while(ch!='-'&&(ch>'9'||ch<'0'))
11         ch=getchar();
12     if(ch=='-')
13         f=-1,ch=getchar();
14     while(ch>='0'&&ch<='9')
15         res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
16     return res*f;
17 }
18 const int N=100;
19 int ans,maxn;
20 int R[N][N],P[N][N],X[N][N];
21 bool f[55][55];
22 void dfs(int d,int x,int y)
23 {
24     if(!y)
25     {
26         if(d-1>maxn)
27         {
28             maxn=d-1;
29             ans=1;
30         }
31         else
32         {
33             if(d-1==maxn)
34                 ans++;
35         }
36         return;
37     }
38     int u=P[d][1];
39     for(RI i=1;i<=y;++i)
40     {
41         int v=P[d][i];
42         if(f[v][u])continue;
43         for(RI j=1;j<=x;++j)
44             R[d+1][j]=R[d][j];
45         R[d+1][x+1]=v;
46         int ty=0,tz=0;
47         for(RI j=1;j<=y;++j)
48             if(f[v][P[d][j]])
49                 P[d+1][++ty]=P[d][j];
50         dfs(d+1,x+1,ty);
51         P[d][i]=0;
52     }
53 }
54 int main()
55 {
56     int n=read(),m=read();
57     for(RI i=1;i<=m;++i)
58     {
59         int x=read(),y=read();
60         f[x][y]=f[y][x]=1;
61     }
62     for(RI i=1;i<=n;++i)
63         P[1][i]=i;
64     dfs(1,0,n);
65     printf("%d %d",maxn,ans);
66     return 0;
67 }
68 //Written By Air_Castle
法2 洛谷 lyh's code

  then  我发现 根本就不需要 R 数组啊

 1 #include<iostream>
 2 #include<cstdio>
 3 #define go(i,a,b) for(register int i=a;i<=b;i++)
 4 using namespace std;
 5 int read()
 6 {
 7     int x=0,y=1;char c=getchar();
 8     while(c<'0'||c>'9') {if(c=='-') y=-1;c=getchar();}
 9     while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+c-'0';c=getchar();}
10     return x*y;
11 }
12 int n,m,ans1,ans2,P[100][100];
13 bool mp[100][100];
14 void dfs(int st,int y) //st:step x:R's num y:P's num
15 {
16     if(!y)
17     {
18         if(st-1>ans1) {ans1=st-1;ans2=1;}
19         else if(st-1==ans1) ans2++;
20         return ;
21     }
22     int u=P[st][1];
23     go(i,1,y)
24     {
25         int v=P[st][i];
26         if(mp[u][v]) continue ;
27         int cnt=0;
28         go(j,1,y)
29             if(mp[v][P[st][j]]) P[st+1][++cnt]=P[st][j];
30         dfs(st+1,cnt);
31         P[st][i]=0;
32     }
33 }
34 int main()
35 {
36     n=read();m=read();
37     go(i,1,m) {int x=read(),y=read();mp[x][y]=mp[y][x]=1;}
38     go(i,1,n) P[1][i]=i;
39     dfs(1,n);
40     printf("%d %d",ans1,ans2);
41     return 0;
42 }
法2 洛谷 dtt's code

   

3. 我还不会 ! ! ! qwq

posted @ 2019-01-26 10:17  DTTTTTTT  阅读(308)  评论(0编辑  收藏  举报