hdu 2196 http://acm.hdu.edu.cn/showproblem.php?pid=2196

input

5//5个结点

1 1//表示结点2到结点1有一条权值为1的边

2 1//表示结点3到结点2有一条权值为1的边

3 1

1 1

要我们求从任意结点出发的最长路径。

思路:一棵树上从某个结点出发的最长路径,肯定是①从该结点出发经由子树到大叶子结点。②从该结点出发经由父结点,然后到大叶子结点。      这两者中的最大者

  ①可以用一遍dfs求出任意结点到叶子结点的最大值。

  ②也是用一遍dfs求出任意结点经由父结点到达叶子结点的最大值

  最后两者取max即可

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
struct computer
{
int v,len;
};
vector<computer> g[N];
int maxn[N],smax[N];
int maxnId[N],smaxId[N];
int up[N];//up[]记录的是从父亲结点转到根结点的最大值
//dfs生成一棵树,然后求的每个结点到叶子结点的最大值和次大值, 最大值和次大值不在一个分支上面
void dfs1(int u, int fa)
{
maxn[u] = smax[u] = ;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i].v;
if(v!=fa)//如果u的一个分支v用来更新最长路,那么就不会更新次长路,所以maxn[u]是u最长的一个分支,smax[v]是v次长的一个分支
//因为是树,所以分支是没有交点的。
//感觉求出来的次长路不是次长路
{
dfs1(v,u);
if(maxn[u] < maxn[v] + g[u][i].len)//最长
{
smax[u] = maxn[u];
maxn[u] = maxn[v] + g[u][i].len;
smaxId[u] = maxnId[u];
maxnId[u] = v;//结点u的最长路是经过结点v这个分支然后到达叶子结点
}
else if(smax[u] < maxn[v] + g[u][i].len)//次长
{
smax[u] = maxn[v] + g[u][i].len;
smaxId[u] = v;
}
}
}
} void dfs2(int u, int fa)
{
for(int i=; i<g[u].size();++i)
{
int v = g[u][i].v;
if(v==fa) continue;
if(v!=maxnId[u])//如果父节点到根结点的最大值不是从v这个分支接入的,那么v从到结点再到根结点的最大值为
{
up[v] = max(up[u],maxn[u])+g[u][i].len;//v到父亲,然后父亲到叶子结点的最大值或者父亲经由父亲的父亲到达叶子结点的最大值
}
else//如果父节点到根结点的最大是从v这个分支接入的,那么fmax[u]+g[u][i].len就会有重叠的部分,所以只能用smax[u]+g[u][i].len
{
up[v] = max(up[u],smax[u]) + g[u][i].len;
}
dfs2(v,u);
}
}
int main()
{
int n,i,x,len;
computer tmp;
while(scanf("%d",&n)!=EOF)
{ for(i=; i<=n; ++i)
g[i].clear();
for(i=; i<=n; ++i)
{
scanf("%d%d",&x,&len);
tmp.v = x;
tmp.len = len;
g[i].push_back(tmp);
tmp.v = i;
g[x].push_back(tmp);
}
dfs1(,-);//设1为根结点,然后dfs,生成dfs树
up[] = ;//更结点没有父亲结点,所以经由父亲结点到达根结点的最大值为0
dfs2(,-); for(i=; i<=n; ++i)
printf("%d\n",max(maxn[i],up[i]));
}
return ;
}

 删点删边类

hdu3586 http://acm.hdu.edu.cn/showproblem.php?pid=3586

给定n和m

然后给定n-1条边和权值

要求删除边使得所有的叶子结点与根结点不连通,删边的花费为边的权值

要求使得花费总和小于m,且所有删边花费中的最大值最小。

思路:dp[u]是使得所有的叶子结点与结点u不连通的最小花费,  dp[u] += min(dp[v],edge(u,v).cost);

  这样子只是求出了删除所有叶子结点的总的最小花费。

  还有一个要求是最大值最小,最大值最小,一般都是用二分枚举最大值。

  那么状态转移方程就改为了  if(edge(u,v).cost > mid)  dp[u] += dp[v];  如果边的权值大于我们枚举的最大值,那么这条边就是不可取的

               else dp[u] +=min(dp[v],edge(u,v).cost);

 //切断某些边,使得所有的叶子结点与根结点不联通,这些权值之和要小于m,且是的所有切断的边中的权值的最大值最小

 /*
dp[i] += min(dp[son],edge(i->son)) 二分枚举边权值的最大值
存在性问题都是用二分枚举
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = ;//这个不能太大,因为有可能所有的边都不可取,然后dp[u] += dp[v] ,那么dp[u]就加了很多的INF
const int N = +;
struct node
{
int v,cost;
};
vector<node> g[N];
int dp[N];
int mid;
int ans; void dfs(int u, int fa)
{
dp[u] = ;
bool flag = false;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i].v;
if(v==fa) continue;
flag = true;
dfs(v,u);
// 如果边的权值大于我们枚举的最大值,那么这条边就是不可取的
if(g[u][i].cost > mid) dp[u] += dp[v];
else
dp[u] += min(dp[v],g[u][i].cost); }
if(!flag)
dp[u] = INF;
}
int main()
{
int n,m,i,a,b,c;
node tmp;
while(scanf("%d%d",&n,&m),n)
{
for(i=; i<=n; ++i)
g[i].clear();
for(i=; i<n; ++i)
{
scanf("%d%d%d",&a,&b,&c);
tmp.cost = c;
tmp.v = a;
g[b].push_back(tmp);
tmp.v = b;
g[a].push_back(tmp);
}
int low = ,high = m;
ans = INF;
while(low<=high)
{
mid = (low + high) >> ;
dfs(,-);
if(dp[]<=m)//如果按照我们枚举的最大值仍然能使得总花费小于m,那么该最大值是可行的
{
ans = mid;
high = mid - ;
}
else
low = mid + ;
}
if(ans == INF)
puts("-1");
else
printf("%d\n",ans);
}
return ;
}

poj3107

给定n和n-1条边。

问删除哪个点,使得剩下的联通分支,结点个数最大的最小。

num[u] 表示子树u有多少个结点,dp[u] 表示删除这个结点后,剩下的最大的联通分支的结点个数

从结点u出发的所有分支获得个数最多的联通分支

(树的重心即删除重心后,会使得最大的联通分支的结点个数最小)

if(v==fa)

dp[u] = max(dp[u],n-num[u]);//

else

dp[u] = max(dp[u],num[v]);

注意:这里用STL会超时。

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
int num[N];
int dp[N];
int n;
int head[N],e;
struct node
{
int v,next;
}g[N*];
void addEdge(int a, int b)
{
g[e].v = b;
g[e].next = head[a];
head[a] = e++;
}
void dfs1(int u, int fa)
{
num[u] = ;
for(int i=head[u]; i!=-; i=g[i].next)
{
int v = g[i].v;
if(v==fa) continue;
dfs1(v,u);
num[u] += num[v];
}
}
void dfs2(int u, int fa)
{
dp[u] = ;
for(int i=head[u]; i!=-; i=g[i].next)
{
int v = g[i].v;
if(v==fa)
{
dp[u] = max(dp[u],n-num[u]);
}
else
{
dfs2(v,u);
dp[u] = max(dp[u],num[v]);
}
}
}
int main()
{
int i,u,v;
while(scanf("%d",&n)!=EOF)
{
e = ;
for(i=; i<=n; ++i)
head[i] = -;
for(i=; i<n; ++i)
{
scanf("%d%d",&u,&v);
addEdge(u,v);
addEdge(v,u);
}
dfs1(,-);
dfs2(,-);
int ans = INF;
for(i=; i<=n; ++i)
ans = min(ans,dp[i]);
for(i=; i<=n; ++i)
if(ans==dp[i])
{
printf("%d",i);
break;
}
for(i+=;i<=n; ++i)
if(ans==dp[i])
printf(" %d",i);
puts(""); }
return ;
}

poj http://poj.org/problem?id=2378

给定n和n-1条边。

问删除哪些点使得最大的联通分支的结点个数小于总结点的一半。

和上面那题一样

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef long long LL;
const int INF = <<;
const int N = + ;
vector<int> g[N];
int num[N],dp[N];
int n;
void dfs1(int u, int fa)
{
num[u] = ;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa) continue;
dfs1(v,u);
num[u] += num[v];
}
}
void dfs2(int u, int fa)
{
dp[u] = ;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa)
{
dp[u] = max(dp[u],n-num[u]);
}
else
{
dp[u] = max(dp[u],num[v]);
dfs2(v,u);
}
}
}
int main()
{
int u,v,i;
while(scanf("%d",&n)!=EOF)
{
for(i=; i<=n; ++i)
g[i].clear();
for(i=; i<n; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(,-);
dfs2(,-);
int half = n / ;
bool flag = false;
for(i=; i<=n; ++i)
if(dp[i] <= half)
{
flag = true;
printf("%d\n",i);
}
if(!flag)
puts("NONE");
}
return ;
}

poj3140 http://poj.org/problem?id=3140

给定n个结点,m条边。

给定n个结点的权值

给定m条边

要求我们删除一条边,树分为两个部分,要求两个部分的权值之差最小。

思路:两次dfs,第一次dfs求出以该结点为根结点的子树的权值

    第二次dfs枚举删除哪条边,然后记录树的两个部分的差值

    最后一次遍历,找出最小的差值即可

  

 #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <iostream>
#include <queue>
#include <stack>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <math.h>
using namespace std;
typedef __int64 LL;
const int INF = <<;
const LL LLINF = ((LL))<<;
const int N = + ;
vector<int> g[N];
LL val[N];
LL dp[N];
LL sum;
//题意:分成两个部分,使得两个部分的权值之差尽可能小
void dfs1(int u, int fa)
{
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa) continue;
dfs1(v,u);
val[u] += val[v];//求出以u为根结点的子树的权值之和
}
}
LL myAbs(LL a)
{
if(a < )
return -a;
return a;
}
void dfs2(int u, int fa)
{
dp[u] = sum;
for(int i=; i<g[u].size(); ++i)
{
int v = g[u][i];
if(v==fa)
{
continue;
}
else//枚举删除边,然后用dp[u]记录最小值
{
dp[u] = min(dp[u],myAbs(sum-val[v]-val[v]));
dfs2(v,u);
}
}
}
int main()
{
int n,m;
int i,u,v;
int t = ;
while(scanf("%d%d",&n,&m),n)
{
sum = ;
for(i=; i<=n; ++i)
{
g[i].clear();
scanf("%I64d",&val[i]);
sum += val[i];
}
for(i=; i<m; ++i)
{
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs1(,-);
dfs2(,-);
LL ans = sum;
for(i=; i<=n; ++i)
ans = min(ans,dp[i]);
printf("Case %d: %I64d\n",t++,ans); }
return ;
}

树形背包类:

选或者不选的01背包类型,那么每个点枚举容量,同时为下一个点枚举容量即可

hdu1011 http://acm.hdu.edu.cn/showproblem.php?pid=1011

给定n个洞穴和m个士兵(每个士兵能消灭20个bugs)

然后给定每个洞穴的bugs数量(背包的费用)和brain的数量(背包的价值)

然后给定n-1条边,使得n个洞穴形成一课树

问能取得的brain数量的最大值。

思路:其实就是在树上面进行背包问题的求解,只不过是有依赖的背包(儿子要选,当且仅当父亲也被选)

题解  :http://www.cnblogs.com/justPassBy/p/4437436.html

hdu1561 http://acm.hdu.edu.cn/showproblem.php?pid=1561

思路:如果a=0,那么就建立一个根结点,让它指向a=0的结点。那么就可以变成一棵树了(同时m++)

变成树,每个结点选或者不选,就是01背包类型的树形dp,和上面那题一样

题解:http://www.cnblogs.com/justPassBy/p/4437849.html

hdu1520 http://acm.hdu.edu.cn/showproblem.php?pid=1520

题意是给定一棵树,每个结点有一个价值,要我们选择任意个结点使得总价值最大,规则是如果父亲结点被选了,那么儿子结点不可以被选,但是儿子的儿子可以被选

dp[u][1] 表示选择结点u可以获得的最大价值

dp[u][0]表示不选择结点u可以获得的最大价值

状态转移方程是 dp[u][1] = max( dp[u][1], dp[u][1]+dp[v][0] ); dp[u][0] = max( dp[u][0],max(dp[u][0]+dp[v][1], dp[u][0]+dp[v][0]) );

题解:http://www.cnblogs.com/justPassBy/p/4439068.html

树形dp专辑的更多相关文章

  1. 【转】【DP_树形DP专辑】【9月9最新更新】【from zeroclock's blog】

    树,一种十分优美的数据结构,因为它本身就具有的递归性,所以它和子树见能相互传递很多信息,还因为它作为被限制的图在上面可进行的操作更多,所以各种用于不同地方的树都出现了,二叉树.三叉树.静态搜索树.AV ...

  2. 【DP_树形DP专题】题单总结

    转载自 http://blog.csdn.net/woshi250hua/article/details/7644959#t2 题单:http://vjudge.net/contest/123963# ...

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

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

  4. poj3417 LCA + 树形dp

    Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 4478   Accepted: 1292 Descripti ...

  5. COGS 2532. [HZOI 2016]树之美 树形dp

    可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...

  6. 【BZOJ-4726】Sabota? 树形DP

    4726: [POI2017]Sabota? Time Limit: 20 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 128  Solved ...

  7. 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)

    题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...

  8. 树形DP

    切题ing!!!!! HDU  2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...

  9. BZOJ 2286 消耗战 (虚树+树形DP)

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...

随机推荐

  1. 14.6.2 Moving or Copying InnoDB Tables to Another Machine 移动或者copy InnoDB 表到另外的机器

    14.6.2 Moving or Copying InnoDB Tables to Another Machine 移动或者copy InnoDB 表到另外的机器 这个章节描述技术关于移动或者copy ...

  2. 几款屏幕录制软件 ActivePresente

    几款屏幕录制软件,最强大是  ActivePresenter ,免费版, 足以应对我们日常需求.列表如下 支持系统:W-Windows,L-Linux,M-Mac 软件 格式 W L M 免费 说明 ...

  3. 10881 - Piotr's Ants

    Problem D Piotr's Ants Time Limit: 2 seconds "One thing is for certain: there is no stopping th ...

  4. 虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

    C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内 ...

  5. Python天天美味(25) - 深入理解yield

    Python天天美味(25) - 深入理解yield - CoderZh - 博客园 Python天天美味(25) - 深入理解yield   yield的英文单词意思是生产,刚接触Python的时候 ...

  6. Inside Qt Series (全集)

    Inside Qt 系列 QObject这个 class 是 QT 对象模型的核心,绝大部分的 QT 类都是从这个类继承而来.这个模型的中心特征就是一个叫做信号和槽(signaland slot)的机 ...

  7. [Android学习笔记]捕获物理回退事件

    物理回退按钮默认情况下是finish当前activity,返回上一个activity 当需要获取物理回退按钮的相应事件时候,可以这么做 步骤如下: 1.override当前activity的onKey ...

  8. 怎么获取Spring的ApplicationContext

    在 WEB 开发中,可能会非常少须要显示的获得 ApplicationContext 来得到由 Spring 进行管理的某些 Bean, 今天我就遇到了,在这里和大家分享一下, WEB 开发中,怎么获 ...

  9. android水平循环滚动控件

    CycleScrollView.java package com.example.test; import android.content.Context; import android.graphi ...

  10. LeetCode My Solution: Minimum Depth of Binary Tree

    Minimum Depth of Binary Tree Total Accepted: 24760 Total Submissions: 83665My Submissions Given a bi ...