2016ACM/ICPC亚洲区沈阳站 E - Counting Cliques(爆搜+剪枝 时限1s)

题目链接:https://nanti.jisuanke.com/t/A2062

题意:

  给出n个点,m条边,求点集大小为S的完全图个数

思路:

  爆搜+剪枝,hdu 5952 把这题时限扩大成4s了,但是这里是1s,网上很多代码都是超时的,剪枝不到位。

  这里剪枝的思想是,将无向变成有向 ,编号小连向编号大的节点。并且记录它们的度,很显然,每搜索一个节点,它的度一定是>=s-1,这样才符合完全图的性质。

  

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=110;
const int maxm=1100;
struct node{
    int u,v,nxt;
}e[maxm];
int h[maxn],vis[maxn];
int n,m,s,t,cnt,ans,num;
int out[maxn],in[maxn];

void init()
{
    memset(out,0,sizeof(out));
    memset(vis,0,sizeof(vis));
    memset(h,-1,sizeof(h));
    cnt=ans=num=0;
}

void add(int u,int v)
{
    e[cnt].v=v,e[cnt].nxt=h[u];
    h[u]=cnt++;
}

void dfs(int u,int k)
{
    if(k==s)//搜索一个答案 
    {
        ans++;
        return ;
    }
    for(int i=h[u];i!=-1;i=e[i].nxt)//记录每一个点入度 
        in[e[i].v]++;
    for(int i=h[u];i!=-1;i=e[i].nxt)
    {
        int v=e[i].v;
        if(vis[v]) continue;
        if(in[v]==k&&in[v]+out[v]>=s-1)//符合情况才可以继续搜索 
        {
            vis[v]=1;
            dfs(v,k+1);
            vis[v]=0;
        }
    }
    for(int i=h[u];i!=-1;i=e[i].nxt)//回溯 
        in[e[i].v]--;
}

int main()
{
    int u,v;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d%d",&n,&m,&s);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&u,&v);
            if(u>v) swap(u,v);//保证编号小的 连向 编号大的 
            out[u]++;//记录出度 
            add(u,v);
        }
        for(int i=1;i<=n;i++)//爆搜每一个点 
        {
            if(out[i]<s-1) continue;//只有当每个点出度>=s-1才可能构成完全图 
            memset(in,0,sizeof(in));
            vis[i]=1;
            dfs(i,1);
            vis[i]=0;//回溯 
        }
        printf("%d\n",ans);
    }
    return 0;
} 
posted @ 2020-01-13 13:20  怀揣少年梦.#  阅读(157)  评论(0编辑  收藏  举报