[Luogu1041] 传染病控制

Description

近来,一种新的传染病肆虐全球。蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延。不幸的是,由于人们尚未完全认识这种传染病,难以准确判别病毒携带者,更没有研制出疫苗以保护易感人群。于是,蓬莱国的疾病控制中心决定采取切断传播途径的方法控制疾病传播。经过 WHO(世界卫生组织)以及全球各国科研部门的努力,这种新兴传染病的传播途径和控制方法已经研究消楚,剩下的任务就是由你协助蓬莱国疾控中心制定一个有效的控制办法。研究表明,这种传染病的传播具有两种很特殊的性质; 第一是它的传播途径是树型的,一个人X只可能被某个特定的人Y感染,只要Y不得病,或者是XY之间的传播途径被切断,则X就不会得病。 第二是,这种疾病的传播有周期性,在一个疾病传播周期之内,传染病将只会感染一代患者,而不会再传播给下一代。 这些性质大大减轻了蓬莱国疾病防控的压力,并且他们已经得到了国内部分易感人群的潜在传播途径图(一棵树)。但是,麻烦还没有结束。由于蓬莱国疾控中心人手不够,同时也缺乏强大的技术,以致他们在一个疾病传播周期内,只能设法切断一条传播途径,而没有被控制的传播途径就会引起更多的易感人群被感染(也就是与当前已经被感染的人有传播途径相连,且连接途径没有被切断的人群)。当不可能有健康人被感染时,疾病就中止传播。所以,蓬莱国疾控中心要制定出一个切断传播途径的顺序,以使尽量少的人被感染。你的程序要针对给定的树,找出合适的切断顺序。

Input

输入格式的第一行是两个整数n(1≤n≤300)和p。接下来p行,每一行有两个整数i和j,表示节点i和j间有边相连(意即,第i人和第j人之间有传播途径相连)。其中节点1是已经被感染的患者。

Output

只有一行,输出总共被感染的人数。

Sample Input

7 6
1 2
1 3
2 4
2 5
3 6
3 7

Sample Output

3

题解

先以\(1\)为根建树,再Dfs枚举,第\(i\)次砍断深度为\(i\)的一条边,统计未被感染的最大人数,输出\(n-Ans\)即可。由于输入为第i人和第j人之间有传播途径相连,但i和j谁是父节点我们并不知道,所以我们先建i与j的双向边,再以\(1\)为根建树后Dfs。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

const int N=500,P=100000;
int n,v[N],d[N][N],maxdp,Ans;//d[i][0]代表深度为i的点的个数,d[i][j]代表深度为i的节点,maxdp记录最大深度
struct edge{int to,nt;}e[P],o[P];//o[]记录输入时的边,e[]记录以1为根建树后的边
int hd[N],ne,head[N],no;//head[],no与o[]为输入边集数组,hd[],ne与e[]为以1为根建树后的边集数组

void Addedge(int x,int y)//建输入边
{
	++no,o[no]=(edge){y,head[x]},head[x]=no;
}
void Read()//输入
{
	int p,x,y;
	scanf("%d%d",&n,&p);
	for(int i=1;i<=p;++i) scanf("%d%d",&x,&y),Addedge(x,y),Addedge(y,x);//注意此处为双向边
}

void Build(int x,int y)//以为父节点x建边
{
	++ne,e[ne]=(edge){y,hd[x]},hd[x]=ne;
}
void Tree(int id)//建以1为树根的树
{
	int next;
	for(int i=head[id];i;i=o[i].nt)
	{
		next=o[i].to;
		if(!v[next])//访问标记,防止从i到j建边后再次从j到i建反向边
		{
			v[next]=1;
			Build(id,next),Tree(next);
		}
	}
}

void Get_dp(int id,int dp)//得出深度
{
	++d[dp][0],d[dp][d[dp][0]]=id;
	if(dp>maxdp) maxdp=dp;
	for(int i=hd[id];i;i=e[i].nt)
		Get_dp(e[i].to,dp+1);
}

int Calc(int id)
{
	int next,Res=0;
	for(int i=hd[id];i;i=e[i].nt)
	{
		next=e[i].to;
		if(!v[next])
		{
			v[next]=1;
			Res+=1+Calc(next);
		}
	}
	return Res;
}

void Back(int id)
{
	int next;
	for(int i=hd[id];i;i=e[i].nt)
	{
		next=e[i].to,v[next]=0;
		Back(next);
	}
}

void Dfs(int dp,int Res)
{
	if(Ans<Res) Ans=Res;
	if(dp>maxdp) return;
	int Sum;
	for(int i=1;i<=d[dp][0];++i)
		if(!v[d[dp][i]])
		{
			Sum=Calc(d[dp][i])+1,//以d[dp][i]为根的树的所有节点均不会被感染
			v[d[dp][i]]=1;
			Dfs(dp+1,Res+Sum);
			v[d[dp][i]]=0;
			Back(d[dp][i]);//回溯
		}
}

int main()
{
	Read();
	v[1]=1;Tree(1);
	Get_dp(1,0);
	memset(v,0,sizeof(v));
	Dfs(1,0),//从深度为1开始,因为1节点(即深度为0的节点)已经被感染
	printf("%d\n",n-Ans);
	return 0;
}
posted @ 2019-08-20 09:02  OItby  阅读(124)  评论(0编辑  收藏  举报