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);
    }
}