1. 程式人生 > >Luogu P1041 傳染病控制(搜尋)

Luogu P1041 傳染病控制(搜尋)

P1041 傳染病控制

題意

題目背景

近來,一種新的傳染病肆虐全球。蓬萊國也發現了零星感染者,為防止該病在蓬萊國大範圍流行,該國政府決定不惜一切代價控制傳染病的蔓延。不幸的是,由於人們尚未完全認識這種傳染病,難以準確判別病毒攜帶者,更沒有研製出疫苗以保護易感人群。於是,蓬萊國的疾病控制中心決定採取切斷傳播途徑的方法控制疾病傳播。經過\(WHO\)(世界衛生組織)以及全球各國科研部門的努力,這種新興傳染病的傳播途徑和控制方法已經研究清楚,剩下的任務就是由你協助蓬萊國疾控中心制定一個有效的控制辦法。

題目描述

研究表明,這種傳染病的傳播具有兩種很特殊的性質;

第一是它的傳播途徑是樹型的,一個人\(X\)

只可能被某個特定的人\(Y\)感染,只要\(Y\)不得病,或者是\(XY\)之間的傳播途徑被切斷,則\(X\)就不會得病。

第二是,這種疾病的傳播有周期性,在一個疾病傳播週期之內,傳染病將只會感染一代患者,而不會再傳播給下一代。

這些性質大大減輕了蓬萊國疾病防控的壓力,並且他們已經得到了國內部分易感人群的潛在傳播途徑圖(一棵樹)。但是,麻煩還沒有結束。由於蓬萊國疾控中心人手不夠,同時也缺乏強大的技術,以致他們在一個疾病傳播週期內,只能設法切斷一條傳播途徑,而沒有被控制的傳播途徑就會引起更多的易感人群被感染(也就是與當前已經被感染的人有傳播途徑相連,且連線途徑沒有被切斷的人群)。當不可能有健康人被感染時,疾病就中止傳播。所以,蓬萊國疾控中心要制定出一個切斷傳播途徑的順序,以使盡量少的人被感染。

你的程式要針對給定的樹,找出合適的切斷順序。

輸入輸出格式

輸入格式:

第一行是兩個整數\(n(1\leq n\leq 300)\)\(p\)
接下來\(p\)行,每一行有\(2\)個整數\(i\)\(j\),表示節點\(i\)\(j\)間有邊相連。(意即,第\(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;
}