zoukankan      html  css  js  c++  java
  • [树形dp] Jzoj P1046 寻宝之旅

    Description

    探险队长凯因意外的弄到了一份黑暗森林的藏宝图,于是,探险队一行人便踏上了寻宝之旅,去寻找传说中的宝藏。
    藏宝点分布在黑暗森林的各处,每个点有一个值,表示藏宝的价值。它们之间由一些小路相连,小路不会形成环,即两个宝藏点之间有且只有一条通路。探险队从其中的一点出发,每次他们可以留一个人在此点开采宝藏,也可以不留,然后其余的人可以分成若干队向这一点相邻的点走去。需要注意的是,如果他们把队伍分成两队或两队以上,就必须留一个人在当前点,提供联络和通讯,当然这个人也可以一边开采此地的宝藏。并且,为了节约时间,队伍在前往开采宝藏的过程中是不会走回头路的。现在你作为队长的助理,已经提供了这幅藏宝图,请你算出探险队所能开采的最大宝藏的价值。

     

    Input

    第一行有两个正整数n(1<=n<=100),表示藏宝点的个数,m(1<=m=100)表示探险队的人数。
    第二行是n个不超过100的正整数,分别表示1到n每个点的宝藏价值。
    接下来的n-1行,每行两个数,x和y(1<=x,y<=n,x<>y),表示藏宝点x,y之间有一条路,数据保证不会有重复的路出现。
    假设一开始探险队在点1处。

    Output

    一个整数,表示探险队所能获得最大的宝藏价值。
     

    Sample Input

    5 3
    1 3 7 2 8
    1 2
    2 3
    1 4
    4 5

    题解

    • 又是一道树形dp,今天题做的要死了
    • 题目说:
    • 它们之间由一些小路相连,小路不会形成环,即两个宝藏点之间有且只有一条通路
    • 显然就是棵树
    • 容易得到设f[i][j]以i为根的子树,剩j的的最大价值
    • 那就有两种情况:
    • ①当前点不挖,直接跳
    • ②当前点留一个人挖,可以向下派j-1个人
    • 则有 f[i][j]=max(f[i][j],f[i][j-1-k]+f[son[i]][k]+v[i])
    • 那么我们不知道在f[i][j-1-k]中是否有v[i],如果有就算重了

    • 可以多定一个数组g,也可以多加一维,其实是一样的

    • g[i][j]表示 ]以i为根的子树,剩j,不挖i 的的最大价值

    代码

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 using namespace std;
     5 int n,m,cnt,v[110],f[110][110],to[210],from[210],head[110],k[110],g[110][110];
     6 void insert(int x,int y) { to[++cnt]=y; from[cnt]=head[x]; head[x]=cnt; }
     7 void dp(int root,int fa)
     8 {
     9     f[root][1]=v[root];
    10     for (int w=head[root];w;w=from[w])
    11         if (to[w]!=fa)
    12         {
    13             dp(to[w],root);
    14             for (int i=1;i<=m;i++) k[i]=max(f[root][i],f[to[w]][i]);
    15             for (int i=1;i<=m;i++)
    16                 for (int j=1;j<i;j++)
    17                     k[i]=max(k[i],g[root][i-j-1]+f[to[w]][j]+v[root]);
    18             for (int i=1;i<=m;i++) f[root][i]=k[i];
    19             for (int i=1;i<=m;i++) k[i]=max(f[to[w]][i],g[root][i]);
    20             for (int i=1;i<=m;i++)
    21                 for (int j=1;j<i;j++)
    22                     k[i]=max(k[i],g[root][i-j]+f[to[w]][j]);
    23             for (int i=1;i<=m;i++) g[root][i]=k[i];
    24         }
    25 }
    26 int main()
    27 {
    28     scanf("%d%d",&n,&m);
    29     for (int i=1;i<=n;i++) scanf("%d",&v[i]);
    30     for (int i=1;i<=n-1;i++)
    31     {
    32         int x,y;
    33         scanf("%d%d",&x,&y);
    34         insert(x,y); insert(y,x);
    35     }
    36     dp(1,0);
    37     printf("%d",f[1][m]);
    38     return 0;
    39 }
  • 相关阅读:
    Java 抽象类
    7.队列的链表实现
    6.队列的数组实现
    5.栈的链表实现
    4.栈的数组实现
    3.线性表-cursor
    2.线性表-Linked list
    1.线性表-Array
    hello world!
    boost 大小端转换
  • 原文地址:https://www.cnblogs.com/Comfortable/p/9277479.html
Copyright © 2011-2022 走看看