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. [置顶] 小伙伴们来自己实现LinkedList

    继前面实现ArrayList后,今天和小伙伴一起实现LinkedList,LinkedList实现我们采用双向链表来实现,在每次查找时候,如果该查找元素位于该链表的前半段,则从开始检索,如果位于链表的 ...

  2. 将Ojective-C代码移植转换为Swift代码

    相比于Objective-C,Swift语言更加简练.有时我们需要把原来写的一些Objective-C代码转换成Swift,下面总结了各种常见的情况. 1,构造函数的迁移 Objective-C为: ...

  3. codeforces 659B Qualifying Contest

    题目链接:http://codeforces.com/problemset/problem/659/B 题意: n个人,m个区.给出n个人的姓名(保证不相同),属于的区域,所得分数.从每个区域中选出成 ...

  4. codeforces 592B The Monster and the Squirrel

    题目链接:http://codeforces.com/contest/592/problem/B 题目分类:数学,找规律 题目分析:重要的是画图找规律   代码: #include<bits/s ...

  5. 3.1.2 MVC模式和URL访问

    本节课大纲: 一.什么是MVC //了解 M -Model 编写model类 对数据进行操作 使用Model类 来操作数据 V -View 编写html文件,页面呈现 C -Controller 编写 ...

  6. AVOS_百度百科

    AVOS_百度百科 AVOS 目录 公司产品 AVOS 是 YouTube 创始人 Chad Hurley 和 Steve Chen(陈士骏)创立的互联网公司.    编辑本段公司产品    产品包括 ...

  7. 飘逸的python - yield简明教程

    发现还有非常多人对yield不理解,云里雾里,于是试着用文字表述. 仅仅要函数含有yield语句,它就返回一个生成器.所以我们与其把其看成函数定义,不如看作是生成器定义.函数用return返回,而生成 ...

  8. 【剑指offer】二叉树的镜像

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/25915971 题目描写叙述: 输入一个二叉树,输出其镜像. 输入: 输入可能包括多个測试例 ...

  9. 扯谈网络编程之自己实现ping

    ping是基于ICMP(Internet Control Message Protocol)协议实现的.而ICMP协议是在IP层实现的. ping实际上是发起者发送一个Echo Request(typ ...

  10. 11gR2RAC环境DBCA创建一个数据库错误ORA-15055 ORA-15001

    11gR2RAC环境DBCA创建一个数据库错误ORA-15055 ORA-15001 象: 在11gR2 GridInfrastructure和Database软件安装完毕之后,运行DBCA创建数据库 ...