题意:
给一个无向图,N(0-100)个顶点,M(0-1000)条边,求图中顶点数为S(1-10)的完全图(任意两个顶点都有一条边相连)的个数。
思路:
最开始的思路就是深搜。但是题目对时间的要求很严格,需要特别注意优化。在我做题的过程中经历了下面几次优化:
0. 限定按照顶点序号上升dfs(完全图性质);
1. 存储边从数组转换为vector(题目说顶点的度最多为20,而顶点数最多为100);
3. 剪枝N-pre < S-deep,当剩下的点不足以构成定点数为S的完全图;(没太大用)
4. for循环初始化改为memset;(挣扎...)
5. path数组由bool改为int,因为每次递归前都要判断当前点是否和所有已经在路径里面点有边相连,所以要挨个去判断,如果path[i]表示点i是否在路径中,每次都需要执行一个100,如果将path[i]更改为路径中第i个点,每次最多执行10;(关键)
6. 删掉了之前用来表示点是否加入路径的数组,因为顶点是严格升序加入路径的,所以这一步判断没必要。
到第五步优化就A掉了,但是代价就是超大的罚时。因为一直在尝试,感觉分析不出来时间复杂度,很被动。。。
代码实现:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <cstring>
using namespace std;
const int MAX_N = 102;
const int MAX_S = 12;
int T;
int res;
int N,M,S;
//记录路径
int path[MAX_N];
bool mm[MAX_N][MAX_N];
vector<int> edge[MAX_N];
void dfs(int pre,int deep);
bool check(int pos,int deep);
int main()
{
scanf("%d",&T);
while( T-- ){
scanf("%d%d%d",&N,&M,&S);
int a,b;
res = 0;
for( int i = 1; i <= N; i++ ){
edge[i].clear();
}
memset(path,0,sizeof(path));
memset(mm,false,sizeof(mm));
for( int i = 0; i < M; i++ ){
scanf("%d%d",&a,&b);
if( a > b ){
swap(a,b);
}
edge[a].push_back(b);
mm[a][b] = true;
mm[b][a] = true;
}
for( int i = 1; i <= N; i++ ){
path[1] = i;
dfs(i,1);
path[1] = 0;
}
printf("%d\n",res);
}
return 0;
}
void dfs(int pre,int deep){
if( deep == S ){
res++;
return ;
}
if( N-pre < S-deep ){
return ;
}
int len = edge[pre].size();
for( int i = 0; i < len; i++ ){
if( check(edge[pre][i],deep) ){
path[deep+1] = edge[pre][i];
dfs(edge[pre][i],deep+1);
path[deep+1] = 0;
}
}
return ;
}
bool check(int pos,int deep){
bool flag = true;
for( int i = 1; i <= deep; i++ ){
if( mm[pos][path[i]]==false ){
flag = false;
break;
}
}
return flag;
}