UVA - 10160 Servicing Stations 剪枝+回溯

来源:互联网 发布:淘宝账号实名认证视频 编辑:程序博客网 时间:2024/04/27 03:52

题目大意:给你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 40int N,M,Min,rec[maxn];int g[maxn][maxn],son[maxn];int cmp(const int &a,const int &b) {returna>b;}void DFS(int cur,int n, int sum) {if(sum >= Min) //剪枝1,如果要覆盖的次数大于最小值的话就剪断return ;if(n == N)//如果符合上面的要求的话,就表名sum必小于MinMin = 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+markfor(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;} 


0 0
原创粉丝点击