树形dp专辑
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专辑的更多相关文章
- 【转】【DP_树形DP专辑】【9月9最新更新】【from zeroclock's blog】
树,一种十分优美的数据结构,因为它本身就具有的递归性,所以它和子树见能相互传递很多信息,还因为它作为被限制的图在上面可进行的操作更多,所以各种用于不同地方的树都出现了,二叉树.三叉树.静态搜索树.AV ...
- 【DP_树形DP专题】题单总结
转载自 http://blog.csdn.net/woshi250hua/article/details/7644959#t2 题单:http://vjudge.net/contest/123963# ...
- 树形动态规划(树形DP)入门问题—初探 & 训练
树形DP入门 poj 2342 Anniversary party 先来个题入门一下~ 题意: 某公司要举办一次晚会,但是为了使得晚会的气氛更加活跃,每个参加晚会的人都不希望在晚会中见到他的直接上 ...
- poj3417 LCA + 树形dp
Network Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 4478 Accepted: 1292 Descripti ...
- COGS 2532. [HZOI 2016]树之美 树形dp
可以发现这道题的数据范围有些奇怪,为毛n辣么大,而k只有10 我们从树形dp的角度来考虑这个问题. 如果我们设f[x][k]表示与x距离为k的点的数量,那么我们可以O(1)回答一个询问 可是这样的话d ...
- 【BZOJ-4726】Sabota? 树形DP
4726: [POI2017]Sabota? Time Limit: 20 Sec Memory Limit: 128 MBSec Special JudgeSubmit: 128 Solved ...
- 树形DP+DFS序+树状数组 HDOJ 5293 Tree chain problem(树链问题)
题目链接 题意: 有n个点的一棵树.其中树上有m条已知的链,每条链有一个权值.从中选出任意个不相交的链使得链的权值和最大. 思路: 树形DP.设dp[i]表示i的子树下的最优权值和,sum[i]表示不 ...
- 树形DP
切题ing!!!!! HDU 2196 Anniversary party 经典树形DP,以前写的太搓了,终于学会简单写法了.... #include <iostream> #inclu ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<= ...
随机推荐
- 【ASP.NET Web API教程】5.1 HTTP消息处理器
原文:[ASP.NET Web API教程]5.1 HTTP消息处理器 注:本文是[ASP.NET Web API系列教程]的一部分,如果您是第一次看本系列教程,请先看前面的内容. 5.1 HTTP ...
- 如何在github中贡献自己的力量
如何参与github的开源项目? 1.找一个发出“pull requests”的项目.有以下几个方法:最简单的方式是,读项目的readme文件.它会告诉你,项目的拥有者是否急切的需要协助.如果read ...
- Swift - 操作表(UIActionSheel)的用法,也叫底部警告框
1,下面创建一个操作表(或叫底部警告框)并弹出显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class ViewController: UIViewC ...
- Python爬行动物(一):基本概念
定义网络爬虫 网络爬虫(Web Spider,也被称为网络蜘蛛,网络机器人,也被称为网页追逐者).按照一定的规则,维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁,自己主动索引 ...
- [Usaco2008 Dec]Patting Heads
It's Bessie's birthday and time for party games! Bessie has instructed the N (1 <= N <= 100,00 ...
- Dnasp计算LD
Dnasp计算LD Table of Contents 1 Dnasp 计算LD 1 Dnasp 计算LD Dnasp有很多的功能,现在主要来记录其计算LD的功能. 首先File——然后打开data— ...
- HNCU1101:马的移动(BFS)
http://hncu.acmclub.com/index.php?app=problem_title&id=111&problem_id=1101 题目描述 小明很喜欢下国际象棋,一 ...
- JAVA中enum的常见用法
JAVA中enum的常见用法包括:定义并添加方法.switch.遍历.EnumSet.EnumMap 1.定义enum并添加或覆盖方法 public Interface Behaviour{ void ...
- Java使用Socket传输文件遇到的问题(转)
1.写了一个socket传输文件的程序,发现传输过去文件有问题.找了一下午终于似乎找到了原因,记录下来警示一下: 接受文件的一端,向本地写文件之前使用Thread.sleep(time)休息一下就解决 ...
- c++中sort()及qsort()的使用方法总结
当并算法具体解释请见点我 想起来自己天天排序排序,冒泡啊,二分查找啊,结果在STL中就自带了排序函数sort,qsort,总算把自己解脱了~ 所以自己总结了一下,首先看sort函数见下表: 函数名 ...