P1041 [NOIP 2003 提高组] 传染病控制

P1041 [NOIP 2003 提高组] 传染病控制 - 洛谷

# P1041 [NOIP 2003 提高组] 传染病控制

## 题目背景

本题是错题,后来被证明没有靠谱的多项式复杂度的做法。测试数据非常的水,各种玄学做法都可以通过,不代表算法正确。因此本题题目和数据仅供参考。

[关于此类题目的详细内容](https://www.luogu.com.cn/paste/pf94n89x)

---

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

## 题目描述

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

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


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


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


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

## 输入格式

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

## 输出格式

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

## 输入输出样例 #1

### 输入 #1

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

### 输出 #1

```
3
```

## 说明/提示

对于 $100\%$ 的数据,$1 \leq n \leq 300$。

**【题目来源】**

NOIP 2003 提高组第四题

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#define LL long long
using namespace std;
int n, p, t1, t2, b[305][305], cnt[305], maxx, dis[305];
bool bol[305], vis[305];
vector <int> k[305], f[305];
struct node{
    int x, quan;
    node (int a, int b) : x(a), quan(b){
    }
    friend bool operator < (node a, node b){
        return a.quan > b.quan;
    }
};
int clean(int i){
    bol[i] = true;
    int num = 1;
    int p = f[i].size();
    for (int j = 0; j < p; ++j){
        num += clean(f[i][j]);
    }
    return num;
} //标记部分
void reclean(int i){
    bol[i] = false;
    int p = f[i].size();
    for (int j = 0; j < p; ++j){
        reclean(f[i][j]);
    }
} //回溯部分
void dfs(int cen, int tot){
    maxx = max(maxx, tot);
    for (int i = 0; i < cnt[cen]; ++i){
        if (!bol[b[cen][i]]){
            int num = clean(b[cen][i]);
            tot += num;
            dfs(cen+1, tot);
            reclean(b[cen][i]);
            tot -= num;
        }
    }
} //dfs核心函数
void resolve(int i, int cen){
    b[cen][cnt[cen]] = i;
    ++cnt[cen];
    int p = k[i].size();
    for (int j = 0; j < p; ++j){
        if (dis[k[i][j]] == dis[i]+1){
            resolve(k[i][j], cen+1);
            f[i].push_back(k[i][j]);
        }
    }
} //预处理第二部分
void solve(){
    priority_queue <node> que;
    for (int i = 0; i <= n; ++i) dis[i] = 999;
    dis[1] = 0;
    que.push(node(1, 0));
    while (!que.empty()){
        node temp = que.top();
        que.pop();
        int x = temp.x;
        int p = k[x].size();
        for (int j = 0; j < p; ++j){
            if (dis[k[x][j]] > dis[x]+1){
                dis[k[x][j]] = dis[x]+1;
                que.push(node(k[x][j], dis[k[x][j]]));
            }
        }
    }
    resolve(1, 0);
} //最短路算法进行预处理
//实际上以节点0开始进行拓扑排序效率更高
int main(){
    scanf("%d %d", &n, &p);
    for (int i = 0; i < p; ++i){
        scanf("%d %d", &t1, &t2);
        k[t1].push_back(t2);
        k[t2].push_back(t1);
    }
    solve();
    dfs(1, 0);
    printf("%d", n-maxx);
    //本人代码量命名较随意见谅pu~
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值