HDOJ-5277 YJC counts stars(DFS)

不难发现,满足条件的点集大小不可能超过4,这样的话进行DFS的深度是很浅的。枚举第一个点,再枚举第二个点,如果第二个点与第一个点有边则继续枚举第三个点,判断是否和前两个点都有边,第四个点同理,并不断更新答案。时间复杂度哦O(n^2),

下面是BC ROUND#46的题解:

首先针对大家提出的m的数据范围的问题,其实对于平面图来说有
  
  
   
   m3n6
  
  ……
首先五个点的团就是平面图判定中提到的
  
  
   
   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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值