• hdu_1011(Starship Troopers) 树形dp


    题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1011

    题意:打洞洞收集脑子,你带领一个军队,洞洞互联成一棵树,每个洞中有一些bug,要全部杀死这些虫子才可以取得这个洞中的脑子,只有杀死当前节点的bug才可以继续走下去,且如果有0个bug你仍要派遣一个士兵在这里,只不过可以士兵不停留。

    题解:很清晰明了的树形dp了,但是某些人说过写题解就要写细致。。。所以我们还是来详细讲解一下树形dp吧。。。

    树形dp:

        这是一个很裸的树形dp,和一般的dp不同的是,树形dp有了分支,所以要考虑子孩子和父亲孩子之间的关系。及父亲的最大值来源于其自孩子反馈给他的结果。

        我们考虑dp[i][j]表示当前以第i个节点为编号的已经考虑的子树种耗费j个士兵的情况可以获得的最大收益

        (分析的时候自顶向下,实现自底向上,这也是dfs的思想,树形dp一般都是在dfs过程中实现的)

        考虑如果我们知道了当前节点所有子孩子的dp值,即我们知道了给当前已经考虑的孩子分着多少个士兵可以获得他和子孩子的最大收益,那么再考虑他的下一个子孩子的时候

        就可以在此基础上枚举将士兵挨个分给新的子孩子的时候的最大收益,最后我们考虑完所有的子孩子,并把所有士兵都分下去就是结果了。

        给出dp方程: dp[i][j] = max(dp[i][j],dp[i][j-k]+dp[son][k]);

        画个图来帮助理解一下:

    是不是很简单腻~图上没有说一些细节,和一般的dp一样,要考虑之前计算的点是否会被覆盖问题,在枚举j时候要从m向前搜索,因为如果从后往前,计算后面dp[i][j]的时候用到dp[i][j-k]已经被修改过了。还有一个细节就是如何保证当前节点有没有bug都分给一个士兵,这个很简单,只要k从1开始循环就可以了,即保证了无论怎样都给这个节点一个士兵。

    代码:

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 using namespace std;
     5 const int N = 110;
     6 int n,m;
     7 int cost[N],value[N];
     8 int vis[N];
     9 struct Edge{
    10     int to;
    11     int next;
    12 }edge[N*N];
    13 int Ecnt;
    14 int head[N];
    15 int dp[N][N];
    16 void init()
    17 {
    18     for(int i = 0; i <= n; i++ ){
    19         head[i] = -1;
    20         vis[i] = 0;
    21     }
    22     Ecnt = 0;
    23     memset(dp,0,sizeof(dp));
    24 }
    25 void add(int from, int to)
    26 {
    27     edge[Ecnt].to = to;
    28     edge[Ecnt].next = head[from];
    29     head[from] = Ecnt++;
    30 
    31     edge[Ecnt].to = from;
    32     edge[Ecnt].next = head[to];
    33     head[to] = Ecnt++;
    34 }
    35 void dfs(int id)
    36 {
    37     vis[id] = 1;
    38     int tm;
    39     if(m<cost[id]) return;
    40     for(int j = cost[id]; j <= m; j++) dp[id][j] = value[id];
    41     for(int d = head[id]; d!=-1; d = edge[d].next)
    42     {
    43         tm = edge[d].to;
    44         if(!vis[tm]){
    45             dfs(tm);
    46             for(int j = m; j >= cost[id]; j--){
    47                 for(int k = 1; k <= (j-cost[id]); k++){
    48                     if(j-k>=cost[id])
    49                     dp[id][j] = max(dp[id][j],dp[id][j-k]+dp[tm][k]);
    50                 }
    51             }
    52         }
    53     }
    54     return;
    55 }
    56 int main()
    57 {
    58     int tm,tm1,tm2;
    59     while(~scanf("%d%d",&n,&m))
    60     {
    61         if(n==-1&&m==-1) return 0;
    62         for(int i = 1; i <= n; i++)
    63         {
    64             scanf("%d %d",&tm,&value[i]);
    65             cost[i] = (tm+19)/20;
    66         }
    67         init();
    68         for(int i = 1; i < n; i++)
    69         {
    70             scanf("%d %d",&tm1,&tm2);
    71             add(tm1,tm2);
    72         }
    73         vis[0] = 1;
    74         if(m==0){printf("0
    ");continue;}
    75         dfs(1);
    76         printf("%d
    ",dp[1][m]);
    77     }
    78     return 0;
    79 }
  • 相关阅读:
    MSSQL Rebuild(重建)索引
    网络爬虫原理
    Twitter Storm:单机环境的安装与配置
    1079 回家
    Win2003 Server磁盘配额揭密之补遗篇
    Win2003 Server磁盘配额揭密之启用篇
    编译mapnik(win7 环境下vs2008编译mapnik 0.7.1 成功)
    Writing a Windows Shell Extension(marco cantu的博客)
    Android学习之一:Cygwin简介
    Linux 进程间通信(一)
  • 原文地址:https://www.cnblogs.com/shanyr/p/6658559.html
Copyright © 2020-2023  润新知