P1041 传染病控制

题目
先来理解题意,我有一个同学,就是题意理解错误,导致他十分烦躁。
题意
人们的关系是一个树形结构,每个节点代表一个人。切断传播途径的方法,实际上就是在每一层中
选择一条边把他切断,那么这条边下面的节点就都是安全的了。

知道了要做什么之后,接下来我们考虑怎么来做。
贪心
下意识的就会想到每次砍最大的那条子树,但这种方法是错误的。所以只好老老实实暴搜。
那暴搜也得有个方法。
思路如下
现在有一颗树
在这里插入图片描述
把第一层的所有节点存在一个数组里。
设该数组为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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值