[NOIP 2003] 传染病控制:创造性地DFS,最优性剪枝

题意:给出一些关系,表示A能把病传染给B,这些关系形成树,每个周期疾病向下传染一层。每个周期开始的时候可以切断一个传染,初始只有1号带病,求最少患病的人数。1<=人数<=300。

开始想贪心或DP,无果,CS告诉我们这道题可以搜索搞定,大家一起研究了一下CS几年前迷一样的Pascal代码。

DFS挺简单,思维的障碍在于我局限于树形DP的模型,总想枚举一下每棵子树分别何时切断,没想到以层为单位搜索。

把整棵树横向建图,每层的结点按子树大小排序,方便进行最优性剪枝。直接DFS可能也行。vector挺方便。

#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAX_N = 300;
vector<int> E[MAX_N+1], F[MAX_N+1];
int n, ans, best = 1<<30, maxd, sz[MAX_N+1], fa[MAX_N+1], dep[MAX_N+1];
bool b[MAX_N+1];

void build(int u, int p, int d)
{
    F[d].push_back(u);
    maxd = max(d, maxd);
    sz[u] = 1;
    fa[u] = p;
    dep[u] = d;
    for (int i = 0; i < E[u].size(); ++i) {
        int v = E[u][i];
        if (v != p) {
            build(v, u, d+1);
            sz[u] += sz[v];
        }
    }
}

bool cmp(int i, int j)
{
    return sz[i] > sz[j];
}

void dfs(int d)
{
    if (ans >= best)
        return;
    int cnt = 0;
    for (int i = 0; i < F[d].size(); ++i) {
        int u = F[d][i];
        if (b[fa[u]])
            ++cnt, b[u] = true;
        else
            b[u] = false;
    }
    if (!cnt) {
        best = min(ans, best);
        return;
    }
    ans += cnt-1;
    // 枚举d层哪个点不被感染
    for (int i = 0; i < F[d].size(); ++i) {
        int u = F[d][i];
        if (b[u]) {
            b[u] = false;
            dfs(d+1);
            b[u] = true;
        }
    }
    ans -= cnt-1;
}

int main()
{
    int p;
    scanf("%d %d", &n, &p);
    for (int i = 0; i < p; ++i) {
        int u, v;
        scanf("%d %d", &u, &v);
        E[u].push_back(v);
        E[v].push_back(u);
    }
    build(1, 0, 0);
    for (int i = 1; i <= maxd; ++i)
        sort(F[i].begin(), F[i].end(), cmp);
    b[1] = true;
    ans = 1;
    dfs(1);
    printf("%d\n", best);
    return 0;
}
    原文作者:传染病问题
    原文地址: https://blog.csdn.net/ruoruo_cheng/article/details/53082248
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞