HDU 5952 Counting Cliques

ACM/ICPC 2016 沈阳站 Counting Cliques
这道题蛮可惜的,其实就是暴力,不过在现场zyyyyy使用了set实现,实际上用vector就过了。

题意

求一个N(N ≤ 100)点M(N ≤ 1000)边的无向图容量为S的团的个数。已知每个点的度不大于20。

思路

代码所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// http://acm.hdu.edu.cn/showproblem.php?pid=5952

#include <iostream>
#include <fstream>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <algorithm>
#include <stack>
#include <functional>
#include "stdlib.h"
#include "time.h"
#include <set>
#include <map>
#include <numeric>
#include <cctype>
#include <cmath>

#define INF 0x3f3f3f3f
#define MAXN 110
#define MOD 1000000000
using namespace std;
#define LL long long
#define ULL unsigned long long
#define LD long double
typedef pair<int, int> pii;

int T;
int n, m, s;

vector<int> mp[MAXN];
int ma[MAXN][MAXN];
int in[MAXN];
bool can[MAXN];
int conn[MAXN];
int sz;
int cnt = 0;

void dfs(int st) {
// dfs任意过程中都是完全图
if (sz >= s) {
cnt++;
return;
}
for (int ii = 0; ii < mp[st].size(); ii++)
//for (int i = 1; i <= n; i++)
{
int i = mp[st][ii];
if (st != i && can[i] && ma[st][i]) {
// (st, i)
bool flag = true;
for (int j = 0; j < sz; j++)
{
if (!ma[i][conn[j]]) {
flag = false;
break;
}
}
if (flag) {
conn[sz] = i;
sz++;
dfs(i);
sz--;
}
}
}
}

int main() {
scanf("%d", &T);
while (T--) {
scanf("%d%d%d", &n, &m, &s);
memset(in, 0, sizeof in);
memset(ma, 0, sizeof ma);
memset(can, 0, sizeof can);
cnt = 0;
for (int i = 0; i <= n; i++)
{
mp[i].clear();
}
for (int i = 0; i < m; i++)
{
int u, v;
scanf("%d%d", &u, &v);
in[u]++; in[v]++;
ma[u][v] = ma[v][u] = true;
if (u > v) swap(u, v); // u <= v
mp[u].push_back(v);
}
for (int i = 1; i <= n; i++)
{
if (in[i] >= s - 1) {
can[i] = true;
}
}
for (int i = 1; i <= n; i++)
{
// important
conn[0] = i;
sz = 1;
dfs(i);
can[i] = false;
}
printf("%d\n", cnt);
}
#ifdef __ACM
system("pause");
#endif
}

进行dfs,dfs维护一个conn数组用来表示一个大小为sz的完全图的所有点。在每层的dfs中每次寻找并添加一个可能的点,这个点必须要满足和已有的完全图能够成一个新的大小为sz+1的完全图(也就是和完全图中的所有其他点的都要有边)。
在实现过程中有以下优化:

  • 为了避免重复,只从边号小到边号大建边
  • 为了节约时间,筛掉度小于S-1的边
  • 为了节约时间,第一层dfs完毕后将该点的从图中去掉。
  • 去掉以上两个优化在hdu上还是能过。