Counting Cliques HDU - 5952 (暴力搜索)

Counting Cliques HDU - 5952 (暴力搜索)

A clique is a complete graph, in which there is an edge between every pair of the vertices. Given a graph with N vertices and M edges, your task is to count the number of cliques with a specific size S in the graph.

Input

The first line is the number of test cases. For each test case, the first line contains 3 integers N,M and S (N ≤ 100,M ≤ 1000,2 ≤ S ≤ 10), each of the following M lines contains 2 integers u and v (1 ≤ u < v ≤ N), which means there is an edge between vertices u and v. It is guaranteed that the maximum degree of the vertices is no larger than 20.

Output

For each test case, output the number of cliques with size S in the graph.

Sample Input

3
4 3 2
1 2
2 3
3 4
5 9 3
1 3
1 4
1 5
2 3
2 4
2 5
3 4
3 5
4 5
6 15 4
1 2
1 3
1 4
1 5
1 6
2 3
2 4
2 5
2 6
3 4
3 5
3 6
4 5
4 6
5 6

Sample Output

3
7
15

题目来源
2016ACM/ICPC亚洲区沈阳站

题意

给出了一个有N个节点,M条边的无向图,求大小为S的完全图的个数

思路

DFS+剪枝
首先要知道完全图:任意两个不同的点都有边一条边相连
所以题目让求大小为S的完全图的时候,可以对一个完全图慢慢加入点,直到这个完全图有S个点,此时 答案 + 1
如何加点呢?
可以先理解单独的一个点自己就是一个S=1的完全图
两个点U、V若有边直接相连,则是个S=2的无向图
若1-2、3-1、3-2,则是个S=3的无向图,因为对于S=2(有点1、2)的无向图来说,有一个点 3 与其集合里的点都相连,所有是个 + 1大小的新完全图。

很明显对于一个无向图,在找这个集合里的点时,首先的前提是有边相连
所以可以想到对于单个点进行DFS的时候,外层循环是所有与它相连的点
然后能把与它相连的点纳入无向图的集合的条件是这个点与当前集合里的点都相连
所以内层循环就是当前集合的大小了

细节处理:题目保证对于每个点的度数不超过20,暗示可以暴力。
在剪枝的时候,对于一个点,如果度数小于S-1则它一定不能与其他S-1个点构成一个大小为S的无向图。
然后在DFS的时候,如果只对一个点DFS肯定在逻辑上就不行,因为答案不够呀
比如对点 1 DFS,这个图可能没有任何其他点与点 1 相连,所以应该对所有的N个点跑一遍DFS,这N遍DFS(最外层的暴力循环),把 i 当做起始点,否则肯定会算重(比如S=3, 1、2、3若两两相连,则可能求出了 3 or 6个无向图:123、132、213、231、312、321(具体几个没试过,总之会算重),而应该只有一个无向图123)。

所以考虑在输入加边的时候,只把点的标号小的作为起点,这样在求解的时候可以避免多算,也不会造成漏算。

AC代码

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define maxn 110
#define maxm 10010
int n,s,m,ans;
bool mp[maxn][maxn];  //存图
int in[maxn];  //点的度数大小
vector<int>v[maxn];  //存边
void DFS(int id, int p[],int si)
{
    if(si==s){  //找到一个
        ans++;
        return ;
    }
    if(in[id]<s-1)  //大小为S的完全图,每个点的度数至少要为s-1
        return ;
    int len=v[id].size();
    for(int i=0;i<len;i++){
        int k=v[id][i];
        bool f=1;
        for(int j=1;j<=si;j++){
            if(!mp[p[j]][k]){
                f=0;
                break;
            }
        }
        if(f){  //如果k与当前集合里的点都相连则加入集合(集合为大小为si的完全图)
            p[++si]=k;
            DFS(k,p,si);
            --si;
        }
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        ans=0;
        int i,a,b;
        memset(mp,0,sizeof(mp));  //初始化
        scanf("%d%d%d",&n,&m,&s);
        for(i=1;i<=n;i++){  //清空
            v[i].clear();
            in[i]=0;
        }
        for(i=1;i<=m;i++){
            scanf("%d %d",&a,&b);
            if(a>b)
                swap(a,b);
            v[a].push_back(b);  //只存一半的图就行
            in[a]++;  //度数+1
            in[b]++;
            mp[a][b]=mp[b][a]=1;  //标记相连
        }
        for(i=1;i<=n;i++){  //以每个点为起始点进行DFS,点的循序从小到大
            int p[maxn];
            int si=1;
            p[1]=i;
            DFS(i,p,si);  //DFS里vector存边也是从小到大,这样减少的循环,也没有漏情况
        }
        printf("%d\n",ans);
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值