题目
先来理解题意,我有一个同学,就是题意理解错误,导致他十分烦躁。
题意
人们的关系是一个树形结构,每个节点代表一个人。切断传播途径的方法,实际上就是在每一层中
选择一条边把他切断,那么这条边下面的节点就都是安全的了。
知道了要做什么之后,接下来我们考虑怎么来做。
贪心
下意识的就会想到每次砍最大的那条子树,但这种方法是错误的。所以只好老老实实暴搜。
那暴搜也得有个方法。
思路如下
现在有一颗树
把第一层的所有节点存在一个数组里。
设该数组为u[]
u[]={1};
然后由这个数组扩展出下一层。
设该数组为to[]
to[]={2,3};
接着从to数组里挑一个点是它不具有扩展能力(即把这棵子树砍断)
1挑2号节点
那么u[]={3};
to[]={6,7,8};
2挑3号节点
那么u[]={2};
to[]={4,5};
由上述的特殊样例可以发现
u[]数组中的全是患病的,
接下来再用数学中从特殊到一般的思想
把第一层和第二层的情况扩展到第i层和第i+1层。
上代码
#include<bits/stdc++.h>
using namespace std;
const int ll=3000;
int n,p,ans;
int head[ll],next[ll],ver[ll],tot,fa[ll];
void add(int u,int v){
tot++;
ver[tot]=v;
next[tot]=head[u];
head[u]=tot;
}//链式前向星存图
void dfs1(int u,int f){
fa[u]=f;
for(int i=head[u]; i ;i=next[i]){
int v=ver[i];
if(v==fa[u]) continue;
dfs1(v,u);
}
}//找出每个节点的父节点
void dfs(int v[],int len,int ran,int del){
//v数组为第i层的头数组,
//del记录了v数组中不是传染源的的那个节点
if(ran>ans) return ;
int zr[330],l=0;//zr记录扩展数组
int pd=0;
for(int i=1;i<=len;i++){
int u=v[i];
if(u==del){pd=1; continue;}
for(int k=head[u];k;k=next[k]){
int to=ver[k];
if(to==fa[u]) continue;
zr[++l]=to;
}
}
for(int i=1;i<=l;i++){
int z=zr[i];//枚举该切哪一条边
dfs(zr,l,ran+len-pd,z);
}
if(l==0) {//若无法扩展出下一层,则说明该层为最底层,
ans=min(ans,ran+len-pd);
return ;
}
}
int main(){
scanf("%d%d",&n,&p);
for(int i=1;i<=p;i++){
int u,v;
scanf("%d%d",&u,&v);
add(u,v);
add(v,u);
}
ans=999999999;
int a[330];
a[1]=1;
dfs1(1,0);
dfs(a,1,0,-1);
printf("%d\n",ans);
return 0;
}