zoukankan      html  css  js  c++  java
  • [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-Ans);
    	return 0;
    }
    

    本文作者:OItby @ https://www.cnblogs.com/hihocoder/

    未经允许,请勿转载。

  • 相关阅读:
    hust 1260 Dominos && hust 1516 Dominos
    poj 1149 PIGS
    hust 1230 beautiful
    poj 1469 COURSES
    hdu 3062 Party
    hust 1027 Enemy Target!
    hdu 3415 Max Sum of Max-K-sub-sequence
    简单的实现轮播代码
    window.parent与window.openner区别介绍
    plupload使用指南(转)
  • 原文地址:https://www.cnblogs.com/hihocoder/p/11380957.html
Copyright © 2011-2022 走看看