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);
}
}
京公网安备 11010502036488号