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. JS实现背景图按时切换或者每次更新

    首先要有一个添加背景图片的div <div  id="myDiv"></div> css样式中添加背景tu图 body{height:100%;} #myD ...

  2. 为什么 MongoDB 连接数被用满了?

    使用 MongoDB 时,可能会遇到因为 mongod 连接数用满了,导致客户端无法连接的问题.mongod的最大连接数通过 net.maxIncomingConnections 指定,默认值为100 ...

  3. SecureCRT超级终端使用说明

    SecureCRT超级终端使用说明 一.连接POS机 1.运行SecureCRT,选择‘文件’菜单,在下拉菜单中选择‘快速连接’菜单: 2.在弹出的对话框中按如下图选择参数: 3.POS端开机,且数据 ...

  4. linux运维/自动化开发__目录

    服务器软件安装 nginx apache php mysql oracle tomcat memcached mongodb sqlserver 常用pc端工具安装使用 Xshell         ...

  5. (转)memcache、redis缓存

    memcache原理.内存模型: http://www.csdn.net/article/2016-03-16/2826609 redis原理: http://baike.baidu.com/link ...

  6. [LAMP]安装-Debian

    sudo apt-get install build-essential sudo apt-get install mysql-server sudo apt-get install apache2 ...

  7. Sublime Text 格式化代码快捷键

    首选项->按键绑定-用户 加入代码: {"keys": ["ctrl+alt+j"], "command": "reinde ...

  8. poj 2914(stoer_wanger算法求全局最小割)

    题目链接:http://poj.org/problem?id=2914 思路:算法基于这样一个定理:对于任意s, t   V ∈ ,全局最小割或者等于原图的s-t 最小割,或者等于将原图进行 Cont ...

  9. plsql类型

    --plsql基本数据类型1.标量类型1.1数字型--BINARY_INTEGER 来存储有符号整数.它的范围是-2**31至2**31.跟PLS_INTEGER一样,BINARY_INTEGER所需 ...

  10. AndroidManifest.xml文件详解(activity)(二)

    android:configChanges 这个属性列出了那些需要Activity进行自我处理的配置变化.当在运行时配置变化发生的时候,默认情况下,这个Activity会被关掉并重启,但是用这个属性声 ...