不难发现,满足条件的点集大小不可能超过4,这样的话进行DFS的深度是很浅的。枚举第一个点,再枚举第二个点,如果第二个点与第一个点有边则继续枚举第三个点,判断是否和前两个点都有边,第四个点同理,并不断更新答案。时间复杂度哦O(n^2),
下面是BC ROUND#46的题解:
首先针对大家提出的m的数据范围的问题,其实对于平面图来说有 m≤3n−6 …… 首先五个点的团就是平面图判定中提到的 K5 ,包含子图 K5 就不是平面图。所以答案只可能是 4 。 那么怎么统计答案呢? 暴力枚举第一个点,再枚举第二个点,在枚举第三个,第四个,要求每个点都与前面其他点联通。你会发现这就过了,为什么呢? 枚举第一个点是 O(n) 的,枚举第二个点是 O(n2) ,但是注意 m=O(n) ,于是枚举第三个点只有 O(n) 次,总次数也是 O(n2) 的。注意到平面图三元环的个数是 O(n) 的,因为把一个点的相邻点按照几角排序,那么这些点的连边相当于是若干个区间,而区间不能相交,所以总共就是 ∑degi=O(m)(degi表示度数) 的。于是枚举第四个点的次数也是 O(n) 的,总复杂度就是 O(n2) 。对于 n=1000 来时完全够了。 标算是怎么写的呢,标算采用了特殊的技巧枚举三元环,先枚举边,然后枚举度数较少的点的相邻点,判断是否与另一个点相邻,这样可以证明是 O(n1.5) 的,至于第四个点,可以 16 个点压一位,预处理出 [0,216) 每个数二进制1的个数,就可以统计了,复杂度是 O(n1.5+n216) ,可以快速通过数据。
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
int n,m,ans,maxm;
bool f[1002][1002];
int q[15];
void DFS(int p,int sum)
{
int i,j,k,t;
if(sum==5) return;
if(sum>maxm)
{
maxm=sum;
ans=1;
}
else if(sum==maxm)
{
ans++;
}
/*
if(sum==3)
{
for(i=1;i<=3; i++) printf("%d ",q[i]);
printf("\n");
}
*/
for(i=p+1;i<=n;i++)
{
k=1;
for(j=1;j<=sum;j++)
if(!f[i][q[j]])
{
k=0;
break;
}
if(k)
{
q[sum+1]=i;
DFS(i,sum+1);
}
}
}
void work()
{
int i,j,k,t,x,y;
memset(f,0,sizeof(f));
memset(q,0,sizeof(q));
maxm=0;
ans=0;
for(i=0;i<n;i++)
scanf("%d%d",&t,&t);
for(i=0;i<m;i++)
{
scanf("%d%d",&x,&y);
f[x][y]=1;
f[y][x]=1;
}
for(i=1;i<=n;i++)
{
q[1]=i;
DFS(i,1);
}
//正在遍历第i个点,集合中已经有1个点了
printf("%d %d\n",maxm,ans);
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
work();
return 0;
}