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

来源:互联网 发布:淘宝分销卖家怎么发货 编辑:程序博客网 时间:2024/04/30 23:47

题意:给出一些关系,表示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;}
0 0
原创粉丝点击