2014-07-18 17:10:26
题意:也算是一个经典的回溯问题。题意不再赘述,来讲讲剪枝:
(1)首先采用邻接表的方式保存图的信息,并对每个点的表进行从大到小的排序。(因为是从第一个点开始DFS,从大到小排序可以使优先考虑后面的点,避免每次都遍历已经考虑的点,以增加效率)
(2)如果当前设立的St数已经超过当前所算的最小值tmin,直接return,后面的已经没有意义。
(3)如果之前已经考虑的点没有被覆盖,而且在当前点及其之后的点也没有与之相连的,那么这个点在之后也不会覆盖,直接return。
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 8 int tmin,n,m; 9 int g[36][36],used[36],sum[36]; 10 11 void Dfs(int cur,int num,int cnt){ //cur:当前处理点,num:St数,cnt:覆盖的点数 12 if(num >= tmin) //当前操作数超当前最小值,剪枝 13 return; 14 if(cnt >= n){ 15 tmin = min(tmin,num); 16 return; 17 } 18 if(cur > n) //已覆盖所有点 19 return; 20 for(int i = 1; i < cur; ++i) //考虑之前的点,且在cur之后没有连接点,剪枝 21 if(used[i] == 0 && g[i][0] < cur) 22 return; 23 int flag = 0; 24 //cur这个点放St 25 for(int i = 0; i < sum[cur]; ++i){ 26 if(used[g[cur][i]] == 0) //存在未覆盖的点 27 ++flag; 28 used[g[cur][i]]++; 29 } 30 if(flag) //若存在未覆盖的点 31 Dfs(cur + 1,num + 1,cnt + flag); 32 //cur这个点不放St 33 for(int i = 0; i < sum[cur]; ++i) 34 used[g[cur][i]]--; 35 Dfs(cur + 1,num,cnt); 36 } 37 38 int main(){ 39 int a,b; 40 while(scanf("%d%d",&n,&m) == 2){ 41 if(!n && !m) break; 42 memset(g,0,sizeof(g)); 43 memset(sum,0,sizeof(sum)); 44 while(m--){ 45 scanf("%d%d",&a,&b); 46 g[a][sum[a]++] = b; 47 g[b][sum[b]++] = a; 48 } 49 for(int i = 1; i <= n; ++i){ 50 g[i][sum[i]++] = i; //之所以要放自己,是因为放了St后自己也被覆盖 51 sort(g[i],g[i] + sum[i],greater<int>()); 52 } 53 tmin = n; 54 Dfs(1,0,0); 55 printf("%d\n",tmin); 56 } 57 return 0; 58 }