【树形dp小练】HDU1520 HDU2196 HDU1561 HDU3534
- 【树形dp】就是在树上做的一些dp之类的递推,由于一般须要递归处理。因此平庸情况的处理可能须要理清思路。昨晚開始切了4题,作为入门训练。题目都很easy。可是似乎做起来都还口以~
hdu1520 Anniversary party
- 给一颗关系树,各点有权值。选一些点出来。任一对直接相连的边连接的点不能同一时候选择,问选择出的权值和最大为多少。
- 考虑以u为根的子树能够选择的方法,dp[u]表示选择u时能获得最大收益。dp2[u]表示不选u时能获得的最大收益。则u不选时,为dp2[u]=max{dp[v],dp2[v]}v是u的孩纸。u要选时。为dp[u]=valueu+max{dp2[v]}v是u的孩纸
/* **********************************************
File Name: 1520.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年08月18日 星期二 19时50分55秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int INF = 0xfffffff;
const int MAX = 6007;
vector<int> G[MAX];
int val[MAX];
int dp[2][MAX];
bool vis[MAX];
int n;
void dfs(int u) {
//printf("dfs %d\n", u);
vis[u] = true;
dp[0][u] = 0;
dp[1][u] = val[u];
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[*it]) {
dfs(*it);
dp[1][u] += max(0, dp[0][*it]);
dp[0][u] += max(0, max(dp[0][*it], dp[1][*it]));
}
}
}
int main() {
while (cin >> n && n) {
for (int i = 1; i <= n; ++i) {
G[i].clear();
cin >> val[i];
}
int x, y;
for (int i = 1; i < n; ++i) {
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
cin >> x >> y; //0 0
//fill(dp[0], dp[0] + n + 1, -INF);
//fill(dp[1], dp[1] + n + 1, -INF);
memset(vis, false, sizeof(vis));
dfs(1); //node 1 is root
/*
for (int i = 1; i <= n; ++i) {
printf("dp[0][%d] = %d, dp[1][%d] = %d\n", i, dp[0][i], i, dp[1][i]);
}
*/
int ans = max(dp[0][1], dp[1][1]);
if (ans == 0) {
ans = -INF;
for (int i = 1; i <= n; ++i) {
if (ans < val[i]) {
ans = val[i];
}
}
}
cout << ans << endl;
}
return 0;
}
hdu2196 Computer
- 一个树型网络,每一个点代表一台Computer。每一个点的Maximum Distance表示该点到树上其他全部点的距离中最长的。求
∑i=1nMDistancei
- 先预处理出各点深度。然后考虑点u的代价,即点u到其他某一点的最长路径,显然分为经过其父亲节点和不经过父亲节点两种。对于前者,我们在遍历时传递一个值fd给它,表示经过父亲节点到其他全部点的距离最长为多少。便可解决。对于后者,仅仅须要考虑u到以u为根的子树中某一叶子的最长距离就可以。于是能够通过u的孩子获得。以下说明怎么维护上述信息。
- 对后一种情况,很easy地就能够做到:对每一个点维护一个值,表示以该点为根的树的高度,最好还是表示为val1u。则val1u=max{val1v+1}v为u的孩纸,初始化为0.
- 对前者。我们考虑fd首先必须包括u的父亲fa到u的这条边的长度,设为len1。然后有两种延伸方式,第一种是向fa的其他若干孩纸中的一个延伸下去。另外一种是向fa的父亲延伸。另外一种延伸显然已经无需额外维护,之前的递归已经处理完成。第一种延伸则提示我们在搜索fa时,须要将fa得到的fd′与fa的其他孩纸的val1值进行对照,取较大者作为fd传递给u,也就是用其他孩纸的val1来动态地更新维护fd值。
这样整个问题就做完了。
- 注意输入
/* **********************************************
File Name: 2196.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年08月19日 星期三 08时35分06秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
/*
* let dp[i][j]表示节点i的孩纸j所在子树的最大深度.
* dp[i][j] = max{dp[j][k]} + 1.
* Then. dfs后继遍历整棵树。父亲为root,遍历孩纸i时,
* 传递max{max{dp[root][k]}(k!=i), fd}和一个深度标记。
* 当中fd为root的父亲传递进来的值。深度标记须要修正。
* 此次扫描就可以得出每一个点的花费
*/
typedef pair<int, int> P;
typedef pair<P, int> PP;
const int MAX = 10007;
vector<P> G[MAX];
multimap<P, int> M[MAX];
int cost[MAX];
bool vis[MAX];
int dfs(int u) {
vis[u] = true;
int res = 0;
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[it->first]) {
int dep = dfs(it->first) + it->second;
if (dep > res) res = dep;
M[u].insert(PP(P(dep, it->first), it->second));
}
}
return res;
}
void gao(int u, int fd) {
//printf("gao %d, with %d\n", u, fd);
//cost[u] = max{fd, M[u].rbegin()->first|M[u].rbegin()-1 ->first}
cost[u] = max(fd, M[u].empty() ? 0 : M[u].rbegin()->first.first);
//printf("cost[%d] = %d\n", u, cost[u]);
if (M[u].empty()) {
return;
} else if (M[u].size() == 1) {
auto it = M[u].begin();
gao(it->first.second, it->second + fd);
} else {
for (auto it = M[u].begin(); it != M[u].end(); ++it) {
for (auto it2 = M[u].rbegin(); it2 != M[u].rend(); ++it2) {
if (it2->first.second != it->first.second) {
//printf("%d != %d\n", it2->first.second, it->first.second);
gao(it->first.second, it->second + max(fd, it2->first.first));
break;
}
}
}
}
}
int main() {
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
G[i].clear();
M[i].clear();
}
int x, y;
for (int i = 2; i <= n; ++i) {
cin >> x >> y;
G[i].push_back(P(x, y));
G[x].push_back(P(i, y));
}
memset(vis, false, sizeof(vis));
dfs(1);
/*
for (int i = 1; i <= n; ++i) {
printf("%d: ", i);
for (auto it = M[i].begin(); it != M[i].end(); ++it) {
printf("<%d,%d> ", it->first.second, it->first.first);
}
puts("");
}
*/
gao(1, 0);
for (int i = 1; i <= n; ++i) {
printf("%d\n", cost[i]);
}
}
return 0;
}
hdu1561 The more, The Better
- 首先简单分析一下。
- 1. 将依赖关系视为有向边,则原关系网络为一个有向图。
- 2. 其次。我们发现随意一个点的入度不会大于1,也就是说,不会同一时候依赖于一个以上的点,原图似乎是一种拓扑的存在。
- 3. 可是要注意,可能出现单向环。也就是出现相似:a→b,b→c,c→a的环,那么我们如果有这样一个环。能够发现:首先,环上随意一点必不可选择;其次,如果真的有这么一个环,由
发现2
可知不会有其他点指向环上随意一点。那么仅仅须要考虑有向外指向的边,这很优点理。我们仅仅须要check一下。不论什么能从该环開始扫描到的点都视为invalid,不合法。
- 依据上面的分析,我们仅仅须要先检查一遍。将全部在上述
单向环
上或者由某一个单向环
可达的点检查出来,兴许处理忽视它们就可以。 - 另一个问题。原图可能是一颗森林,即入度为0的点似乎可能有多个。有一个比較静止的做法是,令
sum=1+∑i=1nvali
添加点0,且val0=sum,由点0向全部入度为0的点连边。求解当前这个图的dp[0][M+1]−(sum+1)就可以。
我们看看为什么能够这么做:
- 首先,根节点权值很大,因此一定能够被选到。一旦有一种方案不选它。收益为v1,则方案
仅仅选择根节点
的收益v2=sum+1一定大于v1。所以M+1个点中一定有一个时根节点。把得到的终于方案中,根节点剔除掉。显然便是正解。并且输入数据对没有依赖的点给出的a=0也为这么做提供了方便
- 首先,根节点权值很大,因此一定能够被选到。一旦有一种方案不选它。收益为v1,则方案
- 处理时,令dp[i][j]表示以i为根,树规模为j的子树的最大收益。那么dp[i][j]=max{dp[i][j−k]+dp[a][k]}a为i的孩纸
/* **********************************************
File Name: 1561.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年08月19日 星期三 10时59分42秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
const int MAX = 207;
const int INF = INT_MAX / MAX;
vector<int> G[MAX];
int val[MAX];
bool vis[MAX];
bool valid[MAX];
int dp[MAX][MAX];
int dp2[MAX];
int n, m;
void check(int u) {
vis[u] = true;
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (vis[*it]) {
valid[u] = false;
}
check(*it);
if (!valid[*it]) valid[u] = false;
}
}
/*
* dp[i][j]: 以节点i为根(必选i)的j个城堡的最大收益.
* dp[i][0] = 0;
*/
void dfs(int u) {
dp[u][0] = 0;
if (!valid[u]) {
return;
}
dp[u][1] = val[u];
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
dfs(*it);
memcpy(dp2, dp[u], sizeof(int) * MAX);
for (int j = 2; j <= m + 1; ++j) {
//printf("check %d\n", j);
for (int k = 1; k < j; ++k) {
dp2[j] = max(dp2[j], dp[*it][k] + dp[u][j - k]);
}
}
memcpy(dp[u], dp2, sizeof(int) * MAX);
}
}
int main() {
while (cin >> n >> m && n) {
for (int i = 0; i <= n; ++i) {
G[i].clear();
}
int a, sum = 0;
for (int i = 1; i <= n; ++i) {
cin >> a >> val[i];
G[a].push_back(i);
sum += val[i];
}
val[0] = sum + 1;
memset(valid, true, sizeof(valid));
memset(vis, false, sizeof(vis));
check(0);
for (int i = 0; i <= n; ++i) {
fill(dp[i], dp[i] + m + 2, -INF);
}
dfs(0);
printf("%d\n", dp[0][m + 1] - sum - 1);
}
return 0;
}
hdu3534 Tree
- 题意:问一颗树的直径d为多少。以及树上有多少路径长度为d
- 首先此题没有给出数据范围,须要我们牺牲几次提交大概度量一下測试数据。检測发现,点数开到10000以上就足够了,全部边长度加起来不会超过long long,但会超过int范围。
- 考虑树上的某一最长路径,设该路径各点中离根近期的为u。
当u就是根节点时,路径长度等于树的高度。当u不为根节点,则考虑以u为根的子树。最长路径就是u到其全部孩子中距离最大的和次大的。分情况讨论就可以。
- 如果我们已经得出不论什么一点到其全部孩子中的最长距离dis[u][0]和次长距离dis[u][1]。则树的直径
d=longest=max{dis[u][0]+dis[u][1]}
对不论什么一点u,维护一颗map<long long, int>记录已经检查完成的孩子的最大长度以及相应的路径数目,如果已经检查完其前c−1个孩纸,当前检查第c个孩纸,最大长度len′,那么仅仅须要在map中查找是否存在longest−len′的长度。如果存在,则组合一下。累加进答案就可以。
/* **********************************************
File Name: 3534.cpp
Auther: zhengdongjian@tju.edu.cn
Created Time: 2015年08月19日 星期三 13时59分25秒
*********************************************** */
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX = 10007;
struct Edge {
int to;
ll dis;
Edge(int to = 0, int dis = 0): to(to), dis(dis) {}
};
vector<Edge> G[MAX];
map<ll, int> M[MAX];
ll dis[MAX][2];
ll longest;
int ans;
bool vis[MAX];
void dfs(int u) {
vis[u] = true;
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[it->to]) {
dfs(it->to);
if (dis[u][0] < dis[it->to][0] + it->dis) {
dis[u][1] = dis[u][0];
dis[u][0] = dis[it->to][0] + it->dis;
} else if (dis[u][1] < dis[it->to][0] + it->dis) {
dis[u][1] = dis[it->to][0] + it->dis;
}
}
}
if (dis[u][0] + dis[u][1] > longest) {
longest = dis[u][0] + dis[u][1];
}
}
void dfs2(int u) {
vis[u] = true;
for (auto it = G[u].begin(); it != G[u].end(); ++it) {
if (!vis[it->to]) {
dfs2(it->to);
int dis1 = M[it->to].rbegin()->first + it->dis;
if (M[u].find(longest - dis1) != M[u].end()) {
ans += M[u].find(longest - dis1)->second * M[it->to].rbegin()->second;
}
M[u][it->dis + M[it->to].rbegin()->first] += M[it->to].rbegin()->second;
//printf("M[%d][%lld] += %d\n", u, it->dis + M[it->to].rbegin()->first, M[it->to].rbegin()->second);
}
}
if (M[u].empty()) {
M[u][0]++;
}
}
int main() {
ios::sync_with_stdio(false);
int n;
while (cin >> n) {
for (int i = 1; i <= n; ++i) {
G[i].clear();
M[i].clear();
}
int u, v, len;
for (int i = 1; i < n; ++i) {
cin >> u >> v >> len;
G[u].push_back(Edge(v, len));
G[v].push_back(Edge(u, len));
}
memset(vis, false, sizeof(vis));
memset(dis, 0, sizeof(dis));
longest = 0LL;
dfs(1);
/*
for (int i = 1; i <= n; ++i) {
printf("dis[%d] = <%lld, %lld>\n", i, dis[i][0], dis[i][1]);
}
*/
//cout << "longest = " << longest << endl;
memset(vis, false, sizeof(vis));
ans = 0;
dfs2(1);
if (M[1].find(longest) != M[1].end()) {
ans += M[1].find(longest)->second;
}
cout << longest << ' ' << ans << endl;
}
return 0;
}
【树形dp小练】HDU1520 HDU2196 HDU1561 HDU3534的更多相关文章
- 数位dp小练
最近刷题的同时还得填填坑,说来你们也不信,我还不会数位dp. 照例推几篇博客: 数位DP讲解 数位dp 的简单入门 这两篇博客讲的都很好,不过代码推荐记搜的形式,不仅易于理解,还短. 数位dp的式子一 ...
- 树形DP基础题 HDU1520
Anniversary party Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others ...
- hdu1520 第一道树形DP,激动哇咔咔!
A - 树形dp Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit Sta ...
- hdu1520 (树形dp)
hdu1520 http://acm.hdu.edu.cn/showproblem.php?pid=1520 题意是给定一棵树,每个结点有一个价值,要我们选择任意个结点使得总价值最大,规则是如果父亲结 ...
- BZOJ4446:[SCOI2015]小凸玩密室(树形DP)
Description 小凸和小方相约玩密室逃脱,这个密室是一棵有n个节点的完全二叉树,每个节点有一个灯泡.点亮所有灯泡即可逃出密室. 每个灯泡有个权值Ai,每条边也有个权值bi.点亮第1个灯泡不需要 ...
- 【BZOJ4711】小奇挖矿 树形DP
[BZOJ4711]小奇挖矿 Description [题目背景] 小奇在喵星系使用了无限非概率驱动的采矿机,以至于在所有星球上都采出了一些矿石,现在它准备建一些矿石仓库并把矿石运到各个仓库里. [问 ...
- 小奇的仓库(树形DP)
「题目背景」 小奇采的矿实在太多了,它准备在喵星系建个矿石仓库.令它无语的是,喵星系的货运飞船引擎还停留在上元时代! 「问题描述」 喵星系有n个星球,星球以及星球间的航线形成一棵树. 从星球a到星球b ...
- BZOJ4446 [Scoi2015]小凸玩密室 【树形Dp】
题目 小凸和小方相约玩密室逃脱,这个密室是一棵有n个节点的完全二叉树,每个节点有一个灯泡.点亮所有灯 泡即可逃出密室.每个灯泡有个权值Ai,每条边也有个权值bi.点亮第1个灯泡不需要花费,之后每点亮4 ...
- hdu1561 The more, The Better (树形dp+背包)
题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=1561 思路:树形dp+01背包 //看注释可以懂 用vector建树更简单. 代码: #i ...
随机推荐
- oracle 时区
select sysdate from dual;select systimestamp from dual;select localtimestamp from dual;select curren ...
- 火狐删除配置文件 会删除目录下所有文件 切记不要把配置文件建立在桌面 恢复软件:易我数据恢复向导 9.0 DiskGenius500
火狐删除配置文件 会删除目录下所有文件 切记不要把配置文件建立在桌面 恢复软件:易我数据恢复向导 9.0 DiskGenius500 结果:由于时间比较常 恢复文件均失败了~
- 获取当前时间(日期格式) && 获取当前加一年的时间(日期格式)
获取当前时间,日期格式function currentDate() { var date = new Date(); var y = date.getFullYear(); var m = date. ...
- ansible相关说明
2.ansible相关说明 2.1.ansible相关命令 ansible:定义并运行简单任务,主要执行ad-hoc命令 ansible-config:查看.编辑.管理ansible配置 ansibl ...
- Java中创建对象的内存图
所有人都知道面向对象思想,Java中的对象的创建在内存中是如何创建的,传智播客的视频看了一遍,把一些讲解的比较清晰的内容记录下来,方便记忆的更加深刻,Java中创建对象的过程,首先要理解JVM中栈.堆 ...
- 洛谷——P2613 【模板】有理数取余
P2613 [模板]有理数取余 读入优化预处理 $\frac {a}{b}\mod 19620817$ 也就是$a\times b^{-1}$ $a\times b^{-1}\mod 19620817 ...
- Hibernate-02
一.hibernate实体创建规则 1.hibernate---->持久层ORM 映射框架,专注于数据的持久化工作. 2.持久化类创建规则 --->1.提供无参数的构造方法 2.私有化.对 ...
- jq ajax请求error: Maximum call stack size exceeded
原因是data中参数iconUrl这个变量未声明导致的.jq在内部循环时报错
- awk输出指定列
awk '{print $0} file' #打印所有列awk '{print $1}' file #打印第一列 awk '{print $1, $3}' file #打印第一和第三列 cat fil ...
- 牛客练习赛29 B 列队
[题解] 把某一行或某一列有4个1的都统计出来,然后首尾接上尽量长的,注意首尾不能选上同一个矩阵,要维护前缀.后缀1最大值和次大值. 还要注意维护矩阵内连续1的长度,因为可能有 0 0 0 0 这种情 ...