最大团 HDU-1530
题目描述
• 给定一个图 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. 搜索 2.
BornKerbosch算法 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 洛谷 90pts Code1 #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 HDU AC Code1 #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 }
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 的必要
法2 洛谷 lyh's code1 //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_Castlethen 我发现 根本就不需要 R 数组啊
法2 洛谷 dtt's code1 #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 }
3. 我还不会 ! ! ! qwq