题目大意:给你N个点,M个相连信息,求最少要在几个点放置服务器才能让所有的点都被覆盖到,放置服务器的点只能把本身和相邻的点覆盖
解题思路:点只存在放还是不放置服务器两中选择,不剪枝的话就会超时,毕竟是2^35,剪枝的话,包括三种情况
1.当前放置服务器的点大于需要放置的最小值,既然大于了最优情况了,就不必要继续在递归下去了
2.放置服务器的点不影响其他点的状态,那么放置该点就没有意义了,因为图联通的
3.出现前面有点无法被覆盖的情况,比如当前已经访问到第四个点了,而1这个点没有被覆盖到,和1有联系的最大的点有2,2<4,说明2已经被访问过了,说明了1和2之间没有放置服务器,而后面的点又影响不到1这个点的状态了。所以将该枝剪掉
简化一下,将和点相连的点列成一个数组,然后将该数组按从大到小的顺序排序,再判断该数组中的最大的点(数组中的第一个)和当前访问点的大小,如果该数组中最大的那个点小于当前访问点且该点没被覆盖,则剪掉,因为后面已经没有点能和该点相连接了
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 40
int N,M,Min,rec[maxn];
int g[maxn][maxn],son[maxn];
int cmp(const int &a,const int &b) {
return a>b;
}
void DFS(int cur,int n, int sum) {
if(sum >= Min) //剪枝1,如果要覆盖的次数大于最小值的话就剪断
return ;
if(n == N)//如果符合上面的要求的话,就表名sum必小于Min
Min = sum;
for(int i = 1; i < cur; i++)
if(!rec[i] && g[i][0] < cur)//剪枝3,
return ;
DFS(cur+1,n,sum);//递归下一个点
int mark = 0;//记录有几个需要存储的,并判断是否剪枝
int vis[maxn];//暂时存储的作用
for(int i = 0; i < son[cur]; i++){//判断和该节点相连接的节点是否有覆盖,如果都已经覆盖了,就剪枝,如果没有覆盖的话,就记录,并让其覆盖
if(rec[g[cur][i]] == 0) {
rec[g[cur][i]] = 1;
vis[mark++] = g[cur][i];
}
}
if(!mark)//判断是否剪枝
return ;
DFS(cur+1,n+mark,sum+1);//如果当前节点符合,就递归,覆盖次数加1,sum+1,覆盖点加mark个,n+mark
for(int i = 0; i < mark; i++)//回溯,将那些被覆盖的点还原
rec[vis[i]] = 0;
}
int main() {
int a, b;
while(scanf("%d%d",&N,&M) && N+M) {
memset(son,0,sizeof(son));
memset(rec,0,sizeof(rec));
memset(g,0,sizeof(g));
for(int i = 0; i < M; i++) {
scanf("%d %d",&a,&b);
g[a][son[a]++] = b;//每一个点列一个数组,后面跟的是和他相连接的点,son记录有几个相连节点有几个
g[b][son[b]++] = a;
}
Min = N + 1;
for(int i = 1; i <= N; i++) {
g[i][son[i]++] = i;
sort(g[i],g[i]+son[i],cmp);//将节点从小到大排序,后面好判断是否需要剪枝
}
DFS(1,0,0);
printf("%d\n",Min);
}
return 0;
}