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回溯累加。
#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)的更多相关文章
- DP专题之概率DP
注意:在概率DP中求期望要逆着推,求概率要正着推 概率DP求期望: 链接: http://acm.hdu.edu.cn/showproblem.php?pid=4405 dp[ i ]表示从i点走到n ...
- 树形dp专题总结
树形dp专题总结 大力dp的练习与晋升 原题均可以在网址上找到 技巧总结 1.换根大法 2.状态定义应只考虑考虑影响的关系 3.数据结构与dp的合理结合(T11) 4.抽直径解决求最长链的许多类问题( ...
- 树形$dp$学习笔记
今天学习了树形\(dp\),一开始浏览各大\(blog\),发现都\(TM\)是题,连个入门的\(blog\)都没有,体验极差.所以我立志要写一篇可以让初学树形\(dp\)的童鞋快速入门. 树形\(d ...
- hdu1011 Starship Troopers 树形DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1011 思路:很明显的树形背包 定义dp[root][m]表示以root为根,派m个士兵的最优解,那么d ...
- HDU 2196.Computer 树形dp 树的直径
Computer Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- 树形dp 入门
今天学了树形dp,发现树形dp就是入门难一些,于是好心的我便立志要发一篇树形dp入门的博客了. 树形dp的概念什么的,相信大家都已经明白,这里就不再多说.直接上例题. 一.常规树形DP P1352 没 ...
- 树形动态规划(树形DP)入门问题—初探 & 训练
树形DP入门 poj 2342 Anniversary party 先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...
- 二叉苹果树|codevs5565|luoguP2015|树形DP|Elena
二叉苹果树 题目描述 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点) 这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1. 我们用一根树枝两端连接的 ...
- 树形DP总结,持续更新
自己做了动态规划的题目已经有了一个月,但是成效甚微,所以来总结一下动态规划,希望自己能够温故知新.这个博客是关于树形dp的,动态规划的一类题目. 首先从最简单的树形DP入手,树形DP顾名思义就是一棵树 ...
随机推荐
- uvalive 3231 Fair Share 公平分配问题 二分+最大流 右边最多流量的结点流量尽量少。
/** 题目: uvalive 3231 Fair Share 公平分配问题 链接:https://vjudge.net/problem/UVALive-3231 题意:有m个任务,n个处理器,每个任 ...
- thinkphp nginx 上配置 并解决get获取到数据现象
server { listen 80; server_name XXXX.funova.net XXX.funova.com; root /opt/newgm; index index.php; lo ...
- python笔记5:装饰器、内置函数、json
装饰器 装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象. 先看简单例子: def run(): time.sleep(1 ...
- MathType可以编辑带圈乘号吗
在数学中有很多符号,可能这些符号我们用得上,也有些符号我们很少用,甚至用不上,但是我们用不上,不代表不存在这个符号,也不代表别人用不上,只是各自所涉及到的知识领域不一样而已.而对于加减乘除运算,几乎每 ...
- hdu3729(二分图)
比赛的时候没有想到二分图,一直在想dp和贪心. 原因是因为看到数据是100000所以直接就没有往二分图匹配上想. 现在想想. 因为二分图两边的太不对称了,60 和100000 , 如果用匈牙利算法考虑 ...
- Jmeter - 分布式部署负载机
1. 原理图: 2.具体操作 ① 负载机 安装JDK.Jmeter[版本与Controller 调度机一致] ② 配置环境变量 ③ 负载机自定义端口号 a.进入Jmeter的bin目录,找到Jmete ...
- 【BZOJ3190】[JLOI2013]赛车 单调栈+几何
[BZOJ3190][JLOI2013]赛车 Description 这里有一辆赛车比赛正在进行,赛场上一共有N辆车,分别称为个g1,g2……gn.赛道是一条无限长的直线.最初,gi位于距离起跑线前进 ...
- iOS 将金钱变为逗号形式
; NSNumberFormatter * formatter = [NSNumberFormatter new]; [formatter setNumberStyle:NSNumberFormatt ...
- mysql返回字符串在另外一个字符串中第n次出现的方法。
SELECT SUBSTRING_INDEX("迟到50分钟,早退15分钟","分钟",2); 返回:迟到50分钟,早退15
- zookeeper简单操作
接下来主要讲述了通过zookeeper服务器自带的zkCli.sh工具模拟客户端访问和操作zookeeper服务器(包括集群服务器). 当成功启动zookeeper服务后,切换到server1/bin ...