A company offers personal computers for sale in N towns (3 <= N <= 35). The towns are denoted by 1, 2, ..., N. There are direct routes connecting M pairs from among these towns. The company decides to build servicing stations in several towns, so that for any town X, there would be a station located either in X or in some immediately neighbouring town of X.
Write a program for finding out the minumum number of stations, which the company has to build, so that the above condition holds.
Input
The input consists of more than one description of town (but totally, less than ten descriptions). Every description starts with number N of towns and number M of pairs of towns directly connected each other. The integers N and M are separated by a space. Every one of the next M rows contains a pair of connected towns, one pair per row. The pair consists of two integers for town's numbers, separated by a space. The input ends with N = 0 and M = 0.
Output
For every town in the input write a line containing the obtained minimum.
An example:
Input:
8 12
1 2
1 6
1 8
2 3
2 6
3 4
3 5
4 5
4 7
5 6
6 7
6 8
0 0
Output:
2
题目大意:给你一个无向图,有n个结点,m个边,现在要建立服务站,服务站可以覆盖与它链接的点,问最少需要几个服务站可以覆盖所有结点。
分析:DFS+剪枝,每个结点有选或者不选两种,不剪枝的话就有2^35。我用的是邻接表存储图,因为根据题目意思这样容易枚举每一个点邻接的结点。
注意的是自己到自己是相联通的,这里错了一次。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #define maxlen 40 6 using namespace std; 7 int maps[maxlen][maxlen]; 8 int s[maxlen]; 9 int visited[maxlen]; 10 int ans; 11 int n,m; 12 bool cmp(int a,int b) 13 { 14 return a>b; 15 } 16 void dfs(int v,int sum,int cnt) 17 { 18 if(sum>=ans)//剪枝3 19 return; 20 if(cnt==n&&ans>sum)//剪枝2 21 { 22 ans=sum; 23 return; 24 } 25 if(v>n)//剪枝1 26 return ; 27 for(int i=1; i<v; ++i) 28 { 29 if(!visited[i]&&maps[i][0]<v) 30 return ; 31 } 32 /*for(int i=1;i<v;++i) 33 { 34 int maxv=0; 35 for(int j=1;j<s[v];++j) 36 { 37 if(maxv<maps[i][j]) 38 maxv=maps[i][j]; 39 } 40 if(maxv<v&&!visited[i]) 41 return ; 42 }*/ 43 int c=0; 44 for(int i=0; i<s[v]; ++i) 45 { 46 if(visited[maps[v][i]]==0) 47 c++; 48 visited[maps[v][i]]++; 49 } 50 if(c) 51 dfs(v+1,sum+1,cnt+c); 52 for(int i=0; i<s[v]; ++i) 53 visited[maps[v][i]]--; 54 dfs(v+1,sum,cnt); 55 } 56 int main () 57 { 58 while(scanf("%d%d",&n,&m)!=EOF) 59 { 60 if(m==0&&n==0) 61 break; 62 memset(maps,0,sizeof(maps)); 63 memset(s,0,sizeof(s)); 64 for(int i=0; i<m; ++i) 65 { 66 int a,b; 67 scanf("%d%d",&a,&b); 68 maps[a][s[a]++]=b; 69 maps[b][s[b]++]=a; 70 } 71 for(int i=1; i<=n; ++i) 72 { 73 maps[i][s[i]++]=i; 74 sort(maps[i],maps[i]+s[i],cmp); 75 } 76 memset(visited,0,sizeof(visited)); 77 ans=n; 78 dfs(1,0,0); 79 printf("%d ",ans); 80 } 81 }
剪枝的话有这么几种:
1、如果当前枚举的点编号大于N当然就返回。
2、当前已经覆盖的结点等于n即全部覆盖了,那么返回答案,出口。
3、当前的服务站个数大于原先的答案,显然后面只会变大,返回。
4、最重要的一个剪枝,T了好几次,当出现前面有的已经无法被覆盖,那么后面不可能再覆盖这个点,返回。
如何实现:
一开始我的是这样的:
1 for(int i=1;i<v;++i) 2 { 3 int maxv=0; 4 for(int j=1;j<s[v];++j) 5 { 6 if(maxv<maps[i][j]) 7 maxv=maps[i][j]; 8 } 9 if(maxv<v&&!visited[i]) 10 return ; 11 }
就是拿编号小于当前结点的结点所有邻接结点中的编号最大的与当前结点比较,如果小于且未被覆盖那么返回。
后来有一种更好的,就是将其排序,其实是一样的更快一点。直接这么判断。
1 for(int i=1; i<v; ++i) 2 { 3 if(!visited[i]&&maps[i][0]<v) 4 return ; 5 }
加了这个剪枝可以将时间缩短到1s左右。
这边还有一个更快的,据说是状态压缩,还不懂,可以将时间缩短到0.1s
1 #include<iostream> 2 using namespace std; 3 #include<stdio.h> 4 #include<string.h> 5 #define MAXN 40 6 #define ONE ((long long) 1) 7 long long st[MAXN], l[MAXN]; 8 9 bool dfs(int n, long long state, int step, int s, int maxlen) 10 { 11 if(state==(ONE<<(n))-1) 12 return true; //all the point can be reached 13 if(step==maxlen) 14 return false; //all the ways searched, still cann't cover all 15 if(s>n) 16 return false; 17 for(int i=s;i<=n;i++) 18 { 19 if((state|l[i])!=(ONE<<(n))-1) 20 break; 21 if((state|st[i])==state) 22 continue; 23 if(dfs(n,state|st[i],step+1,i+1,maxlen)) 24 return true; 25 } 26 return false; 27 } 28 29 int main() 30 { 31 int n, m; 32 while(scanf("%d%d", &n, &m)!=EOF && n+m) 33 { 34 long long state=0; 35 for(int i=1;i<=n;i++) 36 { 37 l[i]=0; 38 st[i]=(ONE<<(i-1)); 39 } 40 int a, b; 41 for(int i=1;i<=m;i++) 42 { 43 scanf("%d%d", &a, &b); 44 st[a]|=(ONE<<(b-1)); 45 st[b]|=(ONE<<(a-1)); 46 } 47 l[n]=st[n]; 48 for(int i=n-1;i>=1;i--) 49 { 50 l[i]=st[i]|l[i+1]; 51 } 52 for(int i=1;i<=n;i++) 53 { 54 if(dfs(n,state,0,1,i)) 55 { 56 printf("%d ", i); 57 break; 58 } 59 } 60 } 61 return 0; 62 }