题意:
给你一个N阶M边的无向图,求图中K阶完全子图(圈图)的个数。
思路:
只能爆搜了,但是要注意剪枝。按无向的做法会超时,改成单调有向的做(只保存小点指向大点的边)
代码:(1762MS)
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;
int T,n,m,ans,k,x,y,to;
const int MAXN=105;
bool imap[MAXN][MAXN]; //imap邻接矩阵,用于直接判断边的关系
int edge[MAXN]; //edge表示关联边的个数
int mark[MAXN]; //mark保存搜索过程中当前子图中的点
int mp[MAXN][MAXN]; //mp邻接表保存边的关系
int check(int P,int step){ //检查新搜索到的点是否满足完全图
for(int i=step;i>0;i--){
if(!imap[mark[i]][P])
return 0;
}
return 1;
}
void dfs(int st,int step){ //爆搜
if(step==k){
ans++;
return ;
}
for(int i=0;i<edge[st];i++){
to=mp[st][i];
if(edge[to]>=k-step-1&&check(to,step)){ //剪枝1:(1)如果比这个点大的点个数小于剩下需要的点的个数则剪枝
mark[step+1]=to; // (2)如果点不符合完全图条件则剪枝
dfs(to,step+1);
}
}
return ;
}
void ini(){
scanf("%d%d%d",&n,&m,&k);
memset(edge,0,sizeof(edge));
memset(imap,0,sizeof(imap));
memset(mp,0,sizeof(mp));
for(int i=0;i<m;i++){
scanf("%d%d",&x,&y);
if(x>y) swap(x,y); //转无向图为有向图
imap[x][y]=1; //只保存小点对大点
mp[x][edge[x]++]=y;
}
return ;
}
void solve(){
ans=0;
for(int i=1;i<=n-k+1;i++){ //剪枝2:由于是小点对大点,所以子图中最小的点最大是n-k+1
if(edge[i]>=k-1){ //剪枝3:同剪枝1(1)
mark[1]=i;
dfs(i,1);
}
}
printf("%d\n",ans);
return ;
}
int main(){
ios::sync_with_stdio(false);
scanf("%d",&T);
while(T--){
ini();
solve();
}
return 0;
}