zoukankan      html  css  js  c++  java
  • poj1947

    树状dp,不简单。

    题意:给出一棵有根树,问最少切断几条边可以得到有i个结点的子树(连通分支)。

    采用左儿子右兄弟的存储方式,

    dp用两个数组:

    f[i][j]表示以i为根的子树,形成以本子树根结点为根(不包含本子树以外结点),共j个结点的子树所需的最小消耗。这里并没有计算切断i与其父亲连接的边的消耗。

    g[i][j]表示以i节点的父亲为根的,i结点以及i的父亲的子结点链中的i以后的结点做根的子树,构成共j个结点的以i的父亲为根(不包含本子树以外结点)的子树所需的最小消耗(i的兄弟们在形成子树时与i的父亲相连,即计算消耗时无需切断与i父亲的连接,但j个结点中不包含i的父亲)

    有状态转移方程:g[root][count] = min(g[tree[root].next][count - i] + f[root][i], g[root][count]);

    f[root][count +1] = g[tree[root].son][count];

    我们只需要把f[i][0]初始化为1,就可以直接把切断整个以root为根的子树的情况融合在状态转移方程中了。意思就是在以root为根的子树中取0个点,需要切断与root的连接,消耗1。

    用递归的方法来填写两个数组。可以形象地认为最外层循环是枚举count,对于每个count,对树进行从下到上,对子树链进行从右到左的填写。

    其实用记忆化搜索的方式会更简便易懂。

    View Code
    #include <iostream>
    #include
    <cstdio>
    #include
    <cstdlib>
    #include
    <cstring>
    usingnamespace std;

    #define maxn 155
    #define inf 0x3f3f3f3f

    struct Node
    {
    int son, next, father, num;
    } tree[maxn];

    int n, p, root;
    int f[maxn][maxn], g[maxn][maxn];

    void work(int root, int count)
    {
    if (root ==0)
    return;
    work(tree[root].son, count);
    work(tree[root].next, count);
    g[root][count]
    = inf;
    if (tree[root].next)
    for (int i =0; i <= count; i++)
    g[root][count]
    = min(g[tree[root].next][count - i] + f[root][i],
    g[root][count]);
    else
    g[root][count]
    = f[root][count];
    f[root][count
    +1] = inf;
    if (count +1> tree[root].num)
    return;
    f[root][count
    +1] = g[tree[root].son][count];
    }

    void cal(int root)
    {
    if (root ==0)
    return;
    cal(tree[root].son);
    cal(tree[root].next);
    int temp = tree[root].son;
    tree[root].num
    =1;
    while (temp)
    {
    tree[root].num
    += tree[temp].num;
    temp
    = tree[temp].next;
    }
    }

    int main()
    {
    //freopen("t.txt", "r", stdin);
    scanf("%d%d", &n, &p);
    memset(tree,
    0, sizeof(tree));
    for (int i =0; i < n -1; i++)
    {
    int a, b;
    scanf(
    "%d%d", &a, &b);
    tree[b].next
    = tree[a].son;
    tree[a].son
    = b;
    tree[b].father
    = a;
    }
    for (int i =1; i <= n; i++)
    if (tree[i].father ==0)
    {
    root
    = i;
    break;
    }
    cal(root);
    for (int i =1; i <= n; i++)
    f[i][
    0] =1;
    for (int i =0; i < p; i++)
    work(root, i);
    int ans = inf;
    for (int i =1; i <= n; i++)
    if (i == root)
    ans
    = min(ans, f[i][p]);
    else
    ans
    = min(ans, f[i][p] +1);
    printf(
    "%d\n", ans);
    return0;
    }
  • 相关阅读:
    WCF简介-01
    专访Bruce Douglass,谈嵌入式经验
    svn查看工程版本库的url地址
    Hello Kiki (中国剩余定理模版)
    中国剩余定理 (CRT)
    Prime Cuts(POJ-1595)
    素数区间筛法
    出现或者反转后出现在每个字符串中的最长子串
    每个字符串至少出现两次且不重复的最长子串
    不小于k个字符串的最长子串
  • 原文地址:https://www.cnblogs.com/rainydays/p/2075391.html
Copyright © 2011-2022 走看看