题目链接:
https://vjudge.net/problem/UVA-10160
题意:
用最少的顶点覆盖尽可能多的城市
思路:
dfs+剪枝+回溯
对于每个城市可以分为选或者不选两种选择,主要在于剪枝的部分。
具体见注释,剪枝2不太好想,如果当前点v之前的点有邻居小于v,那么就停止进一步搜索,因为在之前有小的点没有被覆盖。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
int N,M;
int minv=50;
int vis[40],maxn[40];
int ed[40][40];
void dfs(int v,int num,int cnt){//当前点,已经选中的station数量,已经覆盖的点数
if(num>minv) return;//剪枝1:超过当前最小值,停止下一步搜索
if(cnt>=N){//覆盖完所有顶点,记录这时候的station数量
if(num<minv) minv=num;
return;
}
for(int i=1;i<v;i++){//剪枝2:检查V前面的点i,如果存在i的邻居比当前点v还小,停止进一步搜索
if(!vis[i]&&maxn[i]<v){//maxn[i]包含i本身这个节点
return;
}
}
dfs(v+1,num,cnt);//暂时不选,仅遍历下一个点,不划分station
int flag[40],k=0;
for(int i=1;i<=N;i++){//包括v本身
if(!vis[i]&&ed[v][i]){
flag[k++]=i;//统计当前这个点有多少邻居还没有被覆盖
vis[i]=1;
}
}
if(!k) return;//剪枝3:如果当前点所有邻居都被覆盖,停止下一步搜索
dfs(v+1,num+1,cnt+k);//加入这些邻居,划分station
for(int i=0;i<k;i++) vis[flag[i]]=0;//回溯
}
void init(){
minv=50;
memset(vis,0,sizeof(vis));
memset(maxn,0,sizeof(maxn));
for(int i=1;i<=N;i++){
for(int j=1;j<=N;j++){
ed[i][j]=ed[j][i]=0;
}
ed[i][i]=1;//包括自身
}
}
int main(int argc, char** argv) {
while(scanf("%d %d",&N,&M)!=EOF){
if(N==0&&M==0) break;
init();
for(int i=0;i<M;i++){
int a,b;
scanf("%d %d",&a,&b);
ed[a][b]=1;
ed[b][a]=1;
}
for(int i=1;i<=N;i++){
int maxv=-1;
for(int j=1;j<=N;j++){
if(ed[i][j]&&j>maxv)
maxv=j;
}
maxn[i]=maxv;//记录当前点的最大邻居
}
dfs(1,0,0);
printf("%d\n",minv);
}
return 0;
}