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回溯累加。

 #include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = ;
const int maxe = ;
const int INF = 0x3f3f3f;
int wson[maxn];//以i为根的子树的容量(可选的叶子数目)
int val[maxn];
struct edge
{
int from, to, cost, next;
edge(int ff=,int tt=,int cc=,int nn=):from(ff),to(tt),cost(cc),next(nn){ }
}Edge[maxe];
int Head[maxn],totedge;
int n, m;//1为根,2~n-m为传送器,n-m+1~n为观众(叶子)
int dp[maxn][maxn];//dp[i][j]表示以i为子树选择j个叶子的最大v(叶子值-路值)
bool vis[maxn];//表示该点是否已经经过 void Init()
{
memset(Edge, , sizeof(Edge));
memset(Head, -, sizeof(Head));
memset(dp, -INF, sizeof(dp));
memset(vis, , sizeof(vis));
totedge = ;
} void DP(int now, int pre)
{
vis[now] = true;
if (now >= n - m + )
{//如果为叶子
dp[now][]=,dp[now][] = val[now], wson[now] = ;
return;
}
else dp[now][] = ;
for (int e = Head[now]; e != -; e = Edge[e].next)
{
int u = Edge[e].to, w = Edge[e].cost;
if (vis[u]) continue;
DP(u, now);
wson[now] += wson[u];
for (int j = wson[now]; j >= ; j--)
{//枚举当前结点选择叶子个数(从大往小,01背包)
for (int k = ; k <= min(wson[u],j); k++)
{//枚举子节点的叶子个数
dp[now][j] = max(dp[now][j], dp[now][j - k] + dp[u][k] - w);
}
}
}
} void AddEdge(int from, int to, int cost)
{
Edge[++totedge] = edge(from, to, cost,Head[from]);
Head[from] = totedge;
Edge[++totedge] = edge(to, from, cost, Head[to]);
Head[to] = totedge;
} int main()
{
while (~scanf("%d%d", &n, &m))
{
Init();
for (int i = ; i <= n - m; i++)
{
int k;
scanf("%d", &k);
for (int j = ; j <= k; j++)
{
int to, v;
scanf("%d%d", &to, &v);
AddEdge(i, to, v);
}
}
for (int i = n - m + ; i <= n; i++) scanf("%d", val + i);
DP(, );
for (int i = m; i >= ; i--)
{
if (dp[][i] >= )
{
printf("%d\n",i);
break;
}
}
}
return ;
}

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

 #include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn = ;
const int maxk = ;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxk][];//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])
int Head[maxn],totedge;
struct edge
{
int from, to, next;
edge(int ff=,int tt=,int nn=):from(ff),to(tt),next(nn){ }
}Edge[*maxn]; int N,K;
bool vis[maxn];
int val[maxn]; void AddEdge(int from, int to)
{
Edge[++totedge] = edge(from, to, Head[from]);
Head[from] = totedge;
Edge[++totedge] = edge(to, from, Head[to]);
Head[to] = totedge;
} void Init()
{
memset(dp, , sizeof(dp));
memset(Head, -, sizeof(Head));
memset(vis, , sizeof(vis));
totedge = ;
} void DP(int now)
{
vis[now] = true;
for (int u = Head[now]; u != -; u = Edge[u].next)
{
int v = Edge[u].to;
if (vis[v]) continue;
DP(v);
for (int j = K; j >= ; j--)
{
for (int k = ; k <= j; k++)
{
if (j - k - >= )
{
dp[now][j][] = max(dp[now][j][], dp[now][j - k - ][] + dp[v][k][]);
dp[now][j][] = max(dp[now][j][], dp[v][k][] + dp[now][j - k - ][]);
}
if (j - k - >= ) dp[now][j][] = max(dp[now][j][], dp[now][j - k - ][] + dp[v][k][]);
}
} }
}
int main()
{
while (~scanf("%d%d", &N, &K))
{
Init();
for (int i = ; i <= N; i++)
{
scanf("%d", val+i);
//dp[i][0][0] = dp[i][0][1] = val[i];
for (int j = ; j <= K; j++) dp[i][j][] = dp[i][j][] = val[i];
}
for (int i = ; i < N; i++)
{
int u, v;
scanf("%d%d", &u, &v);
AddEdge(u, v);
}
DP();
//int ans = 0;
//for (int i = 0; i <= K; i++)
//{
// ans = max(ans, max(dp[1][i][0], dp[1][i][1]));
//}
printf("%d\n", max(dp[][K][],dp[][K][]));
}
return ;
}

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。

 #include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std; int n, p;
const int maxn = ;
const int INF = 0x3f3f3f3f;
struct edge
{
int from, to, next;
edge(int ff=,int tt=,int nn=):from(ff),to(tt),next(nn){ }
}Edge[maxn*];
int Head[maxn], totedge;
int dp[maxn][maxn];//dp[i][j]表示以i为根的子树形成j个结点的新树需要删去的最少边数
bool vis[maxn]; void Init()
{
memset(dp,INF, sizeof(dp));
for (int i = ; i <= n; i++) dp[i][] = ;
memset(Head, -, sizeof(Head));
memset(Edge,, sizeof(Edge));
memset(vis, , sizeof(vis));
totedge = ;
} void addEdge(int from, int to)
{
Edge[++totedge] = edge(from, to, Head[from]);
Head[from] = totedge;
Edge[++totedge] = edge(to, from, Head[to]);
Head[to] = totedge;
} void DP(int now)
{
vis[now] = true; for (int i = Head[now]; i != -; i = Edge[i].next)
{
int u = Edge[i].to;
if (vis[u]) continue;
DP(u);
for (int mv = p; mv >= ; mv--)
{
for (int k = ; k < mv; k++)
{//枚举加该子树的结点数
dp[now][mv] = min(dp[now][mv], dp[now][mv - k]- + dp[u][k]);//因为之前并没有添加该子树,所以删除的边数-1,表示添加该子树
}
}
}
}
int main()
{
while (~scanf("%d%d", &n, &p))
{
Init();
for (int i = ; i < n; i++)
{
int from, to;
scanf("%d%d", &from, &to);
addEdge(from, to);
dp[from][]++;
}
DP();//假设1为根结点
int ans = INF;
for (int i = ; i <= n; i++)
{
if(i==)ans = min(ans, dp[i][p]);
else ans = min(ans, dp[i][p] + );//如果非根节点,因为还要删去和父亲所连接的一条边,+1
}
printf("%d\n", ans);
}
return ;
}

DP专题·四(树形dp)的更多相关文章

  1. DP专题之概率DP

    注意:在概率DP中求期望要逆着推,求概率要正着推 概率DP求期望: 链接: http://acm.hdu.edu.cn/showproblem.php?pid=4405 dp[ i ]表示从i点走到n ...

  2. 树形dp专题总结

    树形dp专题总结 大力dp的练习与晋升 原题均可以在网址上找到 技巧总结 1.换根大法 2.状态定义应只考虑考虑影响的关系 3.数据结构与dp的合理结合(T11) 4.抽直径解决求最长链的许多类问题( ...

  3. 树形$dp$学习笔记

    今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...

  4. hdu1011 Starship Troopers 树形DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 思路:很明显的树形背包 定义dp[root][m]表示以root为根,派m个士兵的最优解,那么d ...

  5. HDU 2196.Computer 树形dp 树的直径

    Computer Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  6. 树形dp 入门

    今天学了树形dp,发现树形dp就是入门难一些,于是好心的我便立志要发一篇树形dp入门的博客了. 树形dp的概念什么的,相信大家都已经明白,这里就不再多说.直接上例题. 一.常规树形DP P1352 没 ...

  7. 树形动态规划(树形DP)入门问题—初探 & 训练

    树形DP入门 poj 2342 Anniversary party   先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...

  8. 二叉苹果树|codevs5565|luoguP2015|树形DP|Elena

    二叉苹果树 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的 ...

  9. 树形DP总结,持续更新

    自己做了动态规划的题目已经有了一个月,但是成效甚微,所以来总结一下动态规划,希望自己能够温故知新.这个博客是关于树形dp的,动态规划的一类题目. 首先从最简单的树形DP入手,树形DP顾名思义就是一棵树 ...

随机推荐

  1. 缓存数据库redis、memcached。 MongoDB 资源集锦

    Redis学习资源: 1.Redis学习手册 http://www.cnblogs.com/stephen-liu74/archive/2012/03/14/2349815.html 2.c#连接Re ...

  2. toString() 和 (String) 以及 valueOf() 三者的对照关系[java]

    简述 在Java中,往往需要把一个类型的变量转换成String 类型.作为菜鸟,有时候我会使用(String) data,有时候就使用data.toString(),如果不行还会试试 String.v ...

  3. 【vijos】1790 拓扑编号(拓扑+贪心)

    https://vijos.org/p/1790 好神的贪心题.. 一开始我也想到了贪心,但是是错的..sad 就是因为每一个节点的编号与逆图的子树有关,且编号一定是>=子树的儿子+1的.但是想 ...

  4. 【BZOJ】3399: [Usaco2009 Mar]Sand Castle城堡(贪心)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3399 贪心就是将两组排序,然后直接模拟即可.. 如果我们用a去匹配一个绝对值和它差不多的值,那么去匹 ...

  5. CentOS安装Oracle官方JRE

    CentOS自带的JRE是OpenJDK,因为一些原因,需要换用Oracle官方出品的JRE. Oracle JRE下载地址http://java.com/zh_CN/(下载时注意选择相应版本) 可以 ...

  6. 67、Fragment实现Tab

    <LinearLayout .......... <FrameLayout android:id="@+id/id_content" android:layout_wi ...

  7. 认识tornado(二)

    前面我们对 Tornado 自带的 hello world 作了代码组织上的解释,但是没有更加深入细致地解释.这里我们直接从main()函数开始,单步跟随,看看tornado都干了些什么. 下面是 m ...

  8. darknet(yolov2)移植到caffe框架

    yolov2到caffe的移植主要分两个步骤:一.cfg,weights转换为prototxt,caffemodel1.下载源码:git clone https://github.com/marvis ...

  9. python 清华镜像pip install

    pip install -i https://pypi.tuna.tsinghua.edu.cn/simple

  10. $routeParams传递路由参数

    $routeParams传的值是一个对象数组. 案例: index.html <!DOCTYPE html> <html lang="zh-cn" data-ng ...