【HDU5952】Counting Cliques

题目大意:给定一个\(N\)个点,\(M\)条边的无向图,求图中有多少个大小为\(S\)的团。\(N \le 100,deg(i)\le 20,i\in [1,n]\)

题解:
考虑搜索。
需要确定一种搜索顺序,使得团的计数不重不漏。考虑枚举团中最小编号的节点,且搜索状态转移中只能转移到比当前团中编号最大的节点编号更大的点。

由于\(N\)上限是100,但是每个节点的度数很小,若直接用邻接矩阵进行状态转移,复杂度较高,因此考虑建立邻接表进行转移。在判断两点是否存在边时用邻接矩阵快速判断即可。

由于编号由小到大进行转移,因此对于建图来说,每条边可以仅由编号小的点指向编号大的点,这样转移时又可以省去一半的复杂度。

搜索过程中加入一个剪枝,即:当前团中的点加上后面可以加的所有的点如果小于要求的团的大小,直接返回即可。

代码如下

#include <bits/stdc++.h>

using namespace std;

int main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int T;
  cin >> T;
  while (T--) {
    int n, m, S;
    cin >> n >> m >> S;
    vector<vector<int>> adj(n);
    vector<vector<int>> mat(n, vector<int>(n));
    for (int i = 0; i < m; i++) {
      int x, y;
      cin >> x >> y;
      x--, y--;
      if (x > y) swap(x, y);
      adj[x].push_back(y);
      mat[x][y] = 1;
    }
    int ans = 0;
    vector<int> cyc;
    function<void(int)> dfs = [&](int x) {
      if ((int)cyc.size() == S) {
        ans++;
        return;
      }
      if (n - 1 - x < S - (int)cyc.size()) {
        return;
      }
      for (auto y : adj[x]) {
        bool ok = 1;
        for (auto z : cyc) {
          if (mat[z][y] == 0) {
            ok = 0;
            break;
          }
        }
        if (ok) {
          cyc.push_back(y);
          dfs(y);
          cyc.pop_back();
        }
      }
    };
    for (int i = 0; i < n; i++) {
      cyc.clear();
      cyc.push_back(i);
      dfs(i);
    }
    cout << ans << endl;
  }
  return 0;
}
posted @ 2019-11-10 16:19  shellpicker  阅读(196)  评论(0编辑  收藏  举报