Processing math: 100%

Luogu P1041 传染病控制(搜索)

P1041 传染病控制

题意

题目背景

近来,一种新的传染病肆虐全球。蓬莱国也发现了零星感染者,为防止该病在蓬莱国大范围流行,该国政府决定不惜一切代价控制传染病的蔓延。不幸的是,由于人们尚未完全认识这种传染病,难以准确判别病毒携带者,更没有研制出疫苗以保护易感人群。于是,蓬莱国的疾病控制中心决定采取切断传播途径的方法控制疾病传播。经过WHO(世界卫生组织)以及全球各国科研部门的努力,这种新兴传染病的传播途径和控制方法已经研究清楚,剩下的任务就是由你协助蓬莱国疾控中心制定一个有效的控制办法。

题目描述

研究表明,这种传染病的传播具有两种很特殊的性质;

第一是它的传播途径是树型的,一个人X只可能被某个特定的人Y感染,只要Y不得病,或者是XY之间的传播途径被切断,则X就不会得病。

第二是,这种疾病的传播有周期性,在一个疾病传播周期之内,传染病将只会感染一代患者,而不会再传播给下一代。

这些性质大大减轻了蓬莱国疾病防控的压力,并且他们已经得到了国内部分易感人群的潜在传播途径图(一棵树)。但是,麻烦还没有结束。由于蓬莱国疾控中心人手不够,同时也缺乏强大的技术,以致他们在一个疾病传播周期内,只能设法切断一条传播途径,而没有被控制的传播途径就会引起更多的易感人群被感染(也就是与当前已经被感染的人有传播途径相连,且连接途径没有被切断的人群)。当不可能有健康人被感染时,疾病就中止传播。所以,蓬莱国疾控中心要制定出一个切断传播途径的顺序,以使尽量少的人被感染。

你的程序要针对给定的树,找出合适的切断顺序。

输入输出格式

输入格式:

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

输出格式:

1行,总共被感染的人数。

输入输出样例

输入样例#1:

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

输出样例#1:

3

思路

暴搜,然后来慢慢优化算法。

暴搜的方法也很简单,枚举每次切断的途径,再来判断接下来传染病如何传播,模拟即可。

接下来考虑优化。首先有个贪心的想法:切断当前刚被感染的点,这样一定是最优的切法,因为这样可以保护更多的子树结点。

如果我们不去切树,会发现点被感染的时间其实就是点在树中的深度。所以我们可以先预处理各点的深度,每次就可以直接得到新感染的点有哪些。

再加上最优性剪枝,即如果还没有感染的点已经小于之前搜索得到的答案了,直接返回。其实这么剪枝已经可以AC了,但是实际上如果加上记忆化剪枝,可以大大加快运行速度,不过这个我就没有尝试了。

AC代码

#include<bits/stdc++.h>
using namespace std;
const int MAXN=305;
const int MAXM=10000005;
int n,m,ans=INT_MAX,mxdep,dep[MAXN],sz[MAXN];
int cnt,top[MAXN],to[MAXN<<1],nex[MAXN<<1];
int _cnt,_top[MAXN],_to[MAXM],_nex[MAXM];
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
void dfs1(int now)
{
    mxdep=max(mxdep,dep[now]);
    for(int i=top[now];i;i=nex[i])
    {
        if(sz[to[i]]) continue;
        dep[to[i]]=dep[now]+1,sz[to[i]]=1;
        dfs1(to[i]);
        sz[now]+=sz[to[i]];
    }
}
void dfs2(int dp,int rest,int cut)
{
    if(dp==mxdep)
    {
        ans=min(ans,rest);
        return ;
    }
    int tot=0;_top[dp+1]=0;
    for(int i=_top[dp];i;i=_nex[i])
    {
        if(_to[i]==cut) continue;
        for(int j=top[_to[i]];j;j=nex[j])
            if(dep[to[j]]==dp+1) tot+=sz[to[j]],_to[++_cnt]=to[j],_nex[_cnt]=_top[dp+1],_top[dp+1]=_cnt;
    }
    if(rest-tot>=ans) return ;
    if(!tot)
    {
        ans=min(ans,rest);
        return ;
    }
    for(int i=_top[dp+1];i;i=_nex[i]) dfs2(dp+1,rest-sz[_to[i]],_to[i]);
}
int main()
{
    n=read(),m=read();
    while(m--)
    {
        int x=read(),y=read();
        to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;
        to[++cnt]=x,nex[cnt]=top[y],top[y]=cnt;
    }
    dep[1]=sz[1]=1;
    dfs1(1);
    _to[++_cnt]=1,_nex[_cnt]=_top[1],_top[1]=_cnt;
    dfs2(1,n,0);
    printf("%d",ans);
    return 0;
}
posted @   UranusITS  阅读(115)  评论(0编辑  收藏  举报
编辑推荐:
· 从问题排查到源码分析:ActiveMQ消费端频繁日志刷屏的秘密
· 一次Java后端服务间歇性响应慢的问题排查记录
· dotnet 源代码生成器分析器入门
· ASP.NET Core 模型验证消息的本地化新姿势
· 对象命名为何需要避免'-er'和'-or'后缀
阅读排行:
· “你见过凌晨四点的洛杉矶吗?”--《我们为什么要睡觉》
· 编程神器Trae:当我用上后,才知道自己的创造力被低估了多少
· C# 从零开始使用Layui.Wpf库开发WPF客户端
· 开发的设计和重构,为开发效率服务
· 从零开始开发一个 MCP Server!
点击右上角即可分享
微信分享提示