zoukankan      html  css  js  c++  java
  • DP专题·四(树形dp)

    1、poj 115 TELE

      题意:一个树型网络上有n个结点,1~n-m为信号传送器,n-m+1~n为观众,当信号传送给观众后,观众会付费观看,每铺设一条道路需要一定费用。现在求以1为根,使得收到观众的费用-铺设道路的费用>=0的情况下,能最多给多少个观众观看?

      思路:树形dp,dp[i][j]表示以i为根的子树中选择j个观众(叶子)最大的收益。

      ①如果当前结点为叶子结点,那么其dp[i][0]=0,dp[i][1]=val[i].

      ②如果为其他结点,则dp[i][j]=max(dp[i][j],dp[i][j-k]+dp[son][k]-cost)(i:max->0;j:0->max)(表示当前结点选j个叶子,其中k个叶子来自son这棵子树).每个结点的容量为以其为根的子树的所有叶结点数目,可以通过dp回溯累加。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 using namespace std;
     6 const int maxn = 3010;
     7 const int maxe = 6020;
     8 const int INF = 0x3f3f3f;
     9 int wson[maxn];//以i为根的子树的容量(可选的叶子数目)
    10 int val[maxn];
    11 struct edge
    12 {
    13     int from, to, cost, next;
    14     edge(int ff=0,int tt=0,int cc=0,int nn=0):from(ff),to(tt),cost(cc),next(nn){ }
    15 }Edge[maxe];
    16 int Head[maxn],totedge;
    17 int n, m;//1为根,2~n-m为传送器,n-m+1~n为观众(叶子)
    18 int dp[maxn][maxn];//dp[i][j]表示以i为子树选择j个叶子的最大v(叶子值-路值)
    19 bool vis[maxn];//表示该点是否已经经过
    20 
    21 void Init()
    22 {
    23     memset(Edge, 0, sizeof(Edge));
    24     memset(Head, -1, sizeof(Head));
    25     memset(dp, -INF, sizeof(dp));
    26     memset(vis, 0, sizeof(vis));
    27     totedge = 0;
    28 }
    29 
    30 void DP(int now, int pre)
    31 {
    32     vis[now] = true;
    33     if (now >= n - m + 1)
    34     {//如果为叶子
    35         dp[now][0]=0,dp[now][1] = val[now], wson[now] = 1;
    36         return;
    37     }
    38     else dp[now][0] = 0;
    39     for (int e = Head[now]; e != -1; e = Edge[e].next)
    40     {
    41         int u = Edge[e].to, w = Edge[e].cost;
    42         if (vis[u]) continue;
    43         DP(u, now);
    44         wson[now] += wson[u];
    45         for (int j = wson[now]; j >= 1; j--)
    46         {//枚举当前结点选择叶子个数(从大往小,01背包)
    47             for (int k = 1; k <= min(wson[u],j); k++)
    48             {//枚举子节点的叶子个数
    49                 dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[u][k] - w);
    50             }
    51         }
    52     }
    53 }
    54 
    55 void AddEdge(int from, int to, int cost)
    56 {
    57     Edge[++totedge] = edge(from, to, cost,Head[from]);
    58     Head[from] = totedge;
    59     Edge[++totedge] = edge(to, from, cost, Head[to]);
    60     Head[to] = totedge;
    61 }
    62 
    63 int main()
    64 {
    65     while (~scanf("%d%d", &n, &m))
    66     {
    67         Init();
    68         for (int i = 1; i <= n - m; i++)
    69         {
    70             int k;
    71             scanf("%d", &k);
    72             for (int j = 1; j <= k; j++)
    73             {
    74                 int to, v;
    75                 scanf("%d%d", &to, &v);
    76                 AddEdge(i, to, v);
    77             }
    78         }
    79         for (int i = n - m + 1; i <= n; i++) scanf("%d", val + i);
    80         DP(1, 0);
    81         for (int i = m; i >= 0; i--)
    82         {
    83             if (dp[1][i] >= 0)
    84             {
    85                 printf("%d
    ",i);
    86                 break;
    87             }
    88         }
    89     }
    90     return 0;
    91 }
    View Code

     2、poj 2486 Apple Tree

      题意:有棵树,每个结点上有权值。从结点1出发走K步,求能走到的点的权值之和最大。

      思路:dp[i][j][0]表示从结点i走j步不会到i时的最大值,dp[i][j][1]表示从结点i走j步回到结点i时最大值。

    ①回到结点u:从u出发,要回到u,需要多走两步u-v,v-u,分配给v子树k步,其他子树j-k步,都返回:
        dp[u][j][1]=max(dp[u][j][1],dp[u][j-k-2][1]+dp[v][k][1])
    ②不回到结点u:

      先从其他子树回到u,再在子树v走k步,不回到u:
        dp[u][j][0]=max(dp[u][j][0],dp[u][j-k-1][1]+dp[v][k][0])
      或者先走k步遍历子树v回到u,在从u走j-k-2步到其他子树,不回到u
        dp[u][j][0]=max(dp[u][j][0],dp[v][k][1]+dp[u][j-k-2][0])

    参考:http://www.cnblogs.com/wuyiqi/archive/2012/01/09/2316758.html

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<cstdio>
     5 using namespace std;
     6 const int maxn = 110;
     7 const int maxk = 210;
     8 const int INF = 0x3f3f3f3f;
     9 int dp[maxn][maxk][2];//dp[i][j][0]表示从结点i走j步不会到i时的最大值,dp[i][j][1]表示从结点i走j步回到结点i时最大值。
    10 //回到结点u:从u出发,要回到u,需要多走两步u-v,v-u,分配给v子树k步,其他子树j-k步,都返回:
    11 //dp[u][j][1]=max(dp[u][j][1],dp[u][j-k-2][1]+dp[v][k][1])
    12 //不回到结点u:先从其他子树回到u,再在子树v走k步,不回到u
    13 //dp[u][j][0]=max(dp[u][j][0],dp[u][j-k-1][1]+dp[v][k][0])
    14 //或者先走k步遍历子树v回到u,在从u走j-k-2步到其他子树,不回到u
    15 //dp[u][j][0]=max(dp[u][j][0],dp[v][k][1]+dp[u][j-k-2][0])
    16 int Head[maxn],totedge;
    17 struct edge
    18 {
    19     int from, to, next;
    20     edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){ }
    21 }Edge[2*maxn];
    22 
    23 int N,K;
    24 bool vis[maxn];
    25 int val[maxn];
    26 
    27 void AddEdge(int from, int to)
    28 {
    29     Edge[++totedge] = edge(from, to, Head[from]);
    30     Head[from] = totedge;
    31     Edge[++totedge] = edge(to, from, Head[to]);
    32     Head[to] = totedge;
    33 }
    34 
    35 void Init()
    36 {
    37     memset(dp, 0, sizeof(dp));
    38     memset(Head, -1, sizeof(Head));
    39     memset(vis, 0, sizeof(vis));
    40     totedge = 0;
    41 }
    42 
    43 void DP(int now)
    44 {
    45     vis[now] = true;
    46     for (int u = Head[now]; u != -1; u = Edge[u].next)
    47     {
    48         int v = Edge[u].to;
    49         if (vis[v]) continue;
    50         DP(v);
    51         for (int j = K; j >= 0; j--)
    52         {
    53             for (int k = 0; k <= j; k++)
    54             {
    55                 if (j - k - 2 >= 0)
    56                 {
    57                     dp[now][j][1] = max(dp[now][j][1], dp[now][j - k - 2][1] + dp[v][k][1]);
    58                     dp[now][j][0] = max(dp[now][j][0], dp[v][k][1] + dp[now][j - k - 2][0]);
    59                 }
    60                 if (j - k - 1 >= 0) dp[now][j][0] = max(dp[now][j][0], dp[now][j - k - 1][1] + dp[v][k][0]);
    61             }
    62         }
    63 
    64     }
    65 }
    66 int main()
    67 {
    68     while (~scanf("%d%d", &N, &K))
    69     {
    70         Init();
    71         for (int i = 1; i <= N; i++)
    72         {
    73             scanf("%d", val+i);
    74             //dp[i][0][0] = dp[i][0][1] = val[i];
    75             for (int j = 0; j <= K; j++) dp[i][j][0] = dp[i][j][1] = val[i];
    76         }
    77         for (int i = 1; i < N; i++)
    78         {
    79             int u, v;
    80             scanf("%d%d", &u, &v);
    81             AddEdge(u, v);
    82         }
    83         DP(1);
    84         //int ans = 0;
    85         //for (int i = 0; i <= K; i++)
    86         //{
    87         //    ans = max(ans, max(dp[1][i][0], dp[1][i][1]));
    88         //}
    89         printf("%d
    ", max(dp[1][K][0],dp[1][K][1]));
    90     }
    91     return 0;
    92 }
    View Code

     3、poj 1947 Rebuilding Roads  

      题意:需要把一棵树拆出一个P结点的新子树,问需要的删除的最少边数为多少?

      思路:dp[i][j]表示以i为根的子树形成j个结点的新树需要删去的最少边数。刚开始时,dp[i][1]=以i为根的子树的子树个数(即i的孩子的个数),即当做删除所有孩子。然后,dp[now][mv] = min(dp[now][mv], dp[now][mv - k]-1 + dp[u][k]),表示当考虑其u孩子形成的子树时,需要dp[now][mv - k]-1,因为之前所有dp操作都没有涉及当前子树(即表示删除该子树),现在考虑该子树时需要把连接子树和父结点的边添加回去,即删除的边数-1。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<cstring>
     5 using namespace std;
     6 
     7 int n, p;
     8 const int maxn = 160;
     9 const int INF = 0x3f3f3f3f;
    10 struct edge
    11 {
    12     int from, to, next;
    13     edge(int ff=0,int tt=0,int nn=0):from(ff),to(tt),next(nn){ }
    14 }Edge[maxn*2];
    15 int Head[maxn], totedge;
    16 int dp[maxn][maxn];//dp[i][j]表示以i为根的子树形成j个结点的新树需要删去的最少边数
    17 bool vis[maxn];
    18 
    19 void Init()
    20 {
    21     memset(dp,INF, sizeof(dp));
    22     for (int i = 1; i <= n; i++) dp[i][1] = 0;
    23     memset(Head, -1, sizeof(Head));
    24     memset(Edge,0, sizeof(Edge));
    25     memset(vis, 0, sizeof(vis));
    26     totedge = 0;
    27 }
    28 
    29 void addEdge(int from, int to)
    30 {
    31     Edge[++totedge] = edge(from, to, Head[from]);
    32     Head[from] = totedge;
    33     Edge[++totedge] = edge(to, from, Head[to]);
    34     Head[to] = totedge;
    35 }
    36 
    37 void DP(int now)
    38 {
    39     vis[now] = true;
    40 
    41     for (int i = Head[now]; i != -1; i = Edge[i].next)
    42     {
    43         int u = Edge[i].to;
    44         if (vis[u]) continue;
    45         DP(u);
    46         for (int mv = p; mv >= 1; mv--)
    47         {
    48             for (int k = 1; k < mv; k++)
    49             {//枚举加该子树的结点数
    50                 dp[now][mv] = min(dp[now][mv], dp[now][mv - k]-1 + dp[u][k]);//因为之前并没有添加该子树,所以删除的边数-1,表示添加该子树
    51             }
    52         }
    53     }
    54 }
    55 int main()
    56 {
    57     while (~scanf("%d%d", &n, &p))
    58     {
    59         Init();
    60         for (int i = 1; i < n; i++)
    61         {
    62             int from, to;
    63             scanf("%d%d", &from, &to);
    64             addEdge(from, to);
    65             dp[from][1]++;
    66         }
    67         DP(1);//假设1为根结点
    68         int ans = INF;
    69         for (int i = 1; i <= n; i++)
    70         {
    71             if(i==1)ans = min(ans, dp[i][p]);
    72             else ans = min(ans, dp[i][p] + 1);//如果非根节点,因为还要删去和父亲所连接的一条边,+1
    73         }
    74         printf("%d
    ", ans);
    75     }
    76     return 0;
    77 }
    View Code
  • 相关阅读:
    技术网址收藏
    解决IE兼容模式的方案
    C/S通信模型与B/S通信模型介绍
    asp.net 常用于客户端注册的机器信息
    Ado.Net,关于DataSet和DataTable
    WinForm程序开发
    发送邮件-成功
    css背景全屏-视差
    ASP.NET中的随机密码生成
    javascript 替换 window.onload 的 document.ready
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7767981.html
Copyright © 2011-2022 走看看