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顾名思义就是一棵树 ...
随机推荐
- AppStore苹果应用支付开发(In App Purchase)翻译
http://yarin.blog.51cto.com/1130898/549141 一.In App Purchase概览 Store Kit代表App和App Store之间进行通信.程序将从Ap ...
- java 线程之间的协作 wait()与notifyAll()
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbGlhbmdydWkxOTg4/font/5a6L5L2T/fontsize/400/fill/I0JBQk ...
- Linux上创建SSH隧道
Win上有好用的Xshell,可以做SSH隧道,但是Linux没有很好用的工具,本来gSTM还可以,但是死活装不上,也很久没更新了. 但其实,Linux上直接使用ssh命令就可以创建SSH隧道,非常方 ...
- bjposition
背景位置:background-origin:content-box;//"border-box", "padding-box", "content- ...
- java之路径问题
1.this.class.getClassLoader().getResource("路径").getPath();路径参数不能以“/”开头,获取classes目录下文件的路径 2 ...
- 【Raspberry Pi】webpy+mysql+GPIO 实现手机控制
1.mysql http://dev.mysql.com/doc/refman/5.5/en/index.html 安装 sudo apt-get install update sudo apt-ge ...
- Eclipse UML插件
Green UML http://green.sourceforge.net/ AmaterasUML http://amateras.sourceforge.jp/cgi-bin/fswiki_en ...
- 02、微信小程序的数据绑定
02.微信小程序的数据绑定 目录结构: 模板内容: 使用bindtap绑定事件 <!--index.wxml--> <view class="container" ...
- 【Linux】命令学习笔记和总结
莫名的想学习一下Linux了,因为对这方面的知识储备为0.对于命令行界面始终是零接触零了解,对一个程序员来说这几乎是致命的,所以简单了解一下. 一.教程参考 参考菜鸟教程即可: Linux 教程 | ...
- MD5-【验签】
MD5是什么? MD5是message-digest algorithm 5(信息-摘要算法)的缩写,被广泛用于加密和解密技术上,它可以说是文件的"数字指纹".任何一个文件,无论是 ...