2016ACM/ICPC亚洲区沈阳站 E - Counting Cliques(爆搜+剪枝 时限1s)
题目链接:https://nanti.jisuanke.com/t/A2062
题意:
给出n个点,m条边,求点集大小为S的完全图个数
思路:
爆搜+剪枝,hdu 5952 把这题时限扩大成4s了,但是这里是1s,网上很多代码都是超时的,剪枝不到位。
这里剪枝的思想是,将无向变成有向 ,编号小连向编号大的节点。并且记录它们的度,很显然,每搜索一个节点,它的度一定是>=s-1,这样才符合完全图的性质。
代码:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #include<queue> using namespace std; typedef long long ll; const int maxn=110; const int maxm=1100; struct node{ int u,v,nxt; }e[maxm]; int h[maxn],vis[maxn]; int n,m,s,t,cnt,ans,num; int out[maxn],in[maxn]; void init() { memset(out,0,sizeof(out)); memset(vis,0,sizeof(vis)); memset(h,-1,sizeof(h)); cnt=ans=num=0; } void add(int u,int v) { e[cnt].v=v,e[cnt].nxt=h[u]; h[u]=cnt++; } void dfs(int u,int k) { if(k==s)//搜索一个答案 { ans++; return ; } for(int i=h[u];i!=-1;i=e[i].nxt)//记录每一个点入度 in[e[i].v]++; for(int i=h[u];i!=-1;i=e[i].nxt) { int v=e[i].v; if(vis[v]) continue; if(in[v]==k&&in[v]+out[v]>=s-1)//符合情况才可以继续搜索 { vis[v]=1; dfs(v,k+1); vis[v]=0; } } for(int i=h[u];i!=-1;i=e[i].nxt)//回溯 in[e[i].v]--; } int main() { int u,v; scanf("%d",&t); while(t--) { init(); scanf("%d%d%d",&n,&m,&s); for(int i=0;i<m;i++) { scanf("%d%d",&u,&v); if(u>v) swap(u,v);//保证编号小的 连向 编号大的 out[u]++;//记录出度 add(u,v); } for(int i=1;i<=n;i++)//爆搜每一个点 { if(out[i]<s-1) continue;//只有当每个点出度>=s-1才可能构成完全图 memset(in,0,sizeof(in)); vis[i]=1; dfs(i,1); vis[i]=0;//回溯 } printf("%d\n",ans); } return 0; }