dfs优化
题意:
给定一个n个点、m条边的无向图,要找出图中大小为s的完全图的个数。
思路:
先看一个常规思路,不过会TLE:从所有节点开始都进行一次dfs,将不在集合中并且能与集合中的点形成完全图的点加入集合,集合大小等于s时ans加一,逐层返回。为什么所有节点都要进行一次dfs?因为题目没说图一定是连通图。
不过此算法要进行多次去重,例如当s=2时,1->2和2->1的两次dfs中得到的集合可能相同。所以鉴于上述考虑,我们只选择1->2这一次得到的集合而放弃2->1得到的集合,而如何实现已经暗示得很明显了,就是要从较小点走向较大点。
将原来的无向图改为有向图,从较小的点指向较大的点。为什么这样可行呢?因为最后我们要找的是完全图,完全图中必然有且仅有一条从最小点逐渐走向最大点的路径,根据不同的路径我们就能区分不同的集合。
代码:
#include <iostream> #include <queue> #include <set> #include <map> #include <vector> #include <stack> #include <cmath> #include <algorithm> #include <cstdio> #include <cctype> #include <functional> #include <string> #include <cstring> #include <sstream> #include <deque> #define fir first #define sec second using namespace std; typedef long long ll; typedef pair<int,int> P; typedef pair<P,int> Q; const int inf1=2e9+9; const ll inf2=8e18+9; const ll mol=10007;//1e9+7; const int maxn=1e2+9; const ll maxx=1e12+9; int n,m,s; int edge[maxn][maxn],to[maxn][maxn]; int ans,vis[20],cnt[maxn]; void dfs(int u,int len) { if(len==s) ans++; else if(len+cnt[u]>=s) { for(int i=0;i<cnt[u];i++) { int v=to[u][i],flag=1; for(int j=0;j<len&&flag;j++) if(!edge[min(vis[j],v)][max(vis[j],v)]) flag=0; if(!flag) continue; vis[len]=v; dfs(v,len+1); } } } int main() { int T; scanf("%d",&T); while(T--) { memset(edge,0,sizeof edge); memset(cnt,0,sizeof cnt); scanf("%d%d%d",&n,&m,&s); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); if(u>v) swap(u,v); edge[u][v]=1; to[u][cnt[u]++]=v; } ans=0; for(int i=1;i<=n;i++) vis[0]=i,dfs(i,1); printf("%d\n",ans); } }