算法:uva 1407 Caves (树形背包dp)

题意

一棵n个节点的树,树的边有正整数权,表示两个节点之间的距离.你的任务是回答这样的询问:从跟 节点出发,走不超过x单位的距离,

最多能经过多少节点?同一个节点经过多次, 只能算一个.

思路

这题同样是多天前看的, 在今天才想出解法的. 动态规划就是这么有意思 :)

遍历n个节点, 有两种情 况, 第一种是遍历完之后不回到出发点, 第二种是要回到出发点.

两种都可能会重复经过某些边, 但是显 然还是第二种遍历的花费会更大

在这一题中, 遍历之后不需要回到出发点.

f(i, j, 0): 表示遍 历子树i的j个节点, 不用回到i点的最少花费

f(i, j, 1): 表示遍历子树i的j个节点, 最终又回到i点的最 少花费

f(i, j, 1) = min{ min{ f(i, j-k, 1) + f(v, k, 1) + 2*w | 1<=k<j }  | v 是i的儿子节点}

f(i, j, 0) = min{ min{ min{f(i,j-k,1)+f(v,k,0)+w, f(i,j-k,0)+f(v,k,1) +2*w} | 1<=k<j } | v是i的儿子节点 }

代码

/**=====================================================
* This is a solution for ACM/ICPC problem
*
* @source : uva-1407 Caves
* @description : 树形背包dp
* @author : shuangde
* @blog : blog.csdn.net/shuangde800
* @email : zengshuangde@gmail.com
* Copyright (C) 2013/08/23 14:06 All rights reserved.
*======================================================*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <queue>
#include <cmath>
#include <cstring>
#define MP make_pair
using namespace std;

typedef pair<int, int> PII;
typedef long long int64;
const int INF = 0x3f3f3f3f;

const int MAXN = 510;

vector<PII >adj[MAXN];
int n, q;
int tot[MAXN];
int f[MAXN][MAXN][2];
bool vis[MAXN];

int dfs(int u) {
vis[u] = true;

tot[u] = 1;

// 统计子树节点个数
for (int i = 0; i < adj[u].size(); ++i) {
int v = adj[u][i].first;
int w = adj[u][i].second;
if (vis[v]) continue;
tot[u] += dfs(v);
vis[v] = false;
}

// dp
// www.bianceng.cn
f[u][1][0] = f[u][1][1] = 0;
for (int i = 0; i < adj[u].size(); ++i) {
int v = adj[u][i].first;
int w = adj[u][i].second;
if (vis[v]) continue;
for (int s = tot[u]; s >= 1; --s) {
for (int j = 1; j <= tot[v] && j < s; ++j) {
int tmp1 = f[u][s-j][1] + f[v][j][0] + w;
int tmp2 = f[u][s-j][0] + f[v][j][1] + 2 * w;
f[u][s][0] = min(f[u][s][0], min(tmp1, tmp2));
f[u][s][1] = min(f[u][s][1], f[u][s-j][1] + f[v][j][1] + 2 * w);
}
}
}
return tot[u];
}

int main(){

int cas = 1;
while (~scanf("%d", &n) && n) {

// init
for (int i = 0; i <= n; ++i)
adj[i].clear();

for (int i = 0; i < n - 1; ++i) {
int u, v, w;
scanf("%d%d%d", &v, &u, &w);
adj[u].push_back(MP(v, w));
}

memset(vis, 0, sizeof(vis));
memset(f, INF, sizeof(f));
dfs(0);

scanf("%d", &q);
printf("Case %d:\n", cas++);
while (q--) {
int d;
scanf("%d", &d);
for (int i = n; i >= 1; --i) {
if (f[0][i][0] <= d) {
printf("%d\n", i);
break;
}
}
}
}
return 0;
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索节点
, 整数
, 树形
, 实现树形的遍历
, 解法
java树形菜单遍历
树形dp、树形dp入门、bzoj 树形dp、poj树形dp、树形dp专题,以便于您获取更多的相关知识。

时间: 2024-12-20 17:01:13

算法:uva 1407 Caves (树形背包dp)的相关文章

算法:poj 2486 Apple Tree (树形背包dp)

题意 给一个n个节点的树,节点编号为1~n, 根节点为1, 每个节点有一个权值. 从根节点出发,走 不超过k步,问最多可以获取多少权值? 思路 因为和uva-1407 caves有点相似,所以没想很久就AC 了,但因为初始化问题WA了两次 f(i, j, 0): 表示子树i,走j次,最终不用回到i点获取的最大总权值 f(i, j, 1): 表示子树i,走j次,最终一定要回到i点获取的最大总权值 f(i, j, 1) = min{ min{ f(i, j-k, 1) + f(v, k-2, 1)

算法:poj 1155 TELE (树形背包dp)

题意 某收费有线电视网计划转播一场重要的足球比赛.他们的转播网和用户终端构成一棵树状结 构,这棵树的根结点位于足球比赛的现场,树叶为各个用户终端,其他中转站为该树的内部节点. 从 转播站到转播站以及从转播站到所有用户终端的信号传输费用都是已知的,一场转播的总费用等于传输信号 的费用总和. 现在每个用户都准备了一笔费用想观看这场精彩的足球比赛,有线电视网有权决定给哪 些用户提供信号而不给哪些用户提供信号. 写一个程序找出一个方案使得有线电视网在不亏本的情况 下使观看转播的用户尽可能多. 思路 树形

算法:zoj 3201 Tree of Tree(树形背包dp)

题意 给一棵节点带权的树,找到一个有k个节点的子树,求这个子树的最大权值 思路 树形 dp+背包. f(i, j) 表示以i为根节点的有j个节点子树的最大权值 然后对i的每个子节点做分组背包, 因为对于i的每个儿子,可以选择分配 1,2,3...j-1个节点给它 f(i, j) = max{ max{f(i, j-p) + f(v, p) | 1<=p<j} | v是i的儿子节点} ans = max{ f[i][k] | 0<=i<n && i 子树节点个数>

算法:poj 3345 Bribing FIPA (树形背包dp | 输入坑)

题意 有n个国家,你要获取m个国家的支持,获取第i个国家的支持就要给cost[i]的价钱 其中有 一些国家是老大和小弟的关系,也就是说,如果你获得了某个老大国家的支持, 那么这个国家的所有小弟 (包括小弟的小弟...递归下去)都会无偿免费支持你. 问最少的花费可以得到m个国家的支持 思路 这题还是比较好想的树形dp, 不过输入有些麻烦, 一开始以为每组样例结束都是'#',结果一直 RE,后来发现最后一组才是 '#'... 国家由于是直接给名字的,所以我用map<string, int>来映射保

算法:poj 1947 Rebuilding Roads (树形背包dp)

题意 给一棵树,问最少删掉几条边.使得剩下的子树中有节点个数为p个的 思路 几天前就看了 这题, 但是没什么想法,之后每天都有去想一下, 直到今天, 在我对自己方法还有怀疑 的情况下,竟然AC了 .. f(i, j) 表示子树i,保留j个节点的最少删边次数, 注意,这里保留的j个节点的子树,是指根节点 为i的且有j个节点的子树.这样理解的话, 状态转移就容易想多了. 对于子树i, 如果只保留1个节 点,那么连接它所有儿子节点的边都要删掉, 所以可以初始化 f(i, 1) = 节点i的儿子个数 f

算法:hdu 1561 The more, The Better (树形背包dp)

题目 ACboy很喜欢玩一种战略游戏,在一个地图上,有N座城堡,每座城堡都有一定的宝物,在每 次游戏中ACboy允许攻克M个城堡并获得里面的宝物.但由于地理位置原因,有些城堡不能直接攻克,要攻克 这些城堡必须先攻克其他某一个特定的城堡.你能帮ACboy算出要获得尽量多的宝物应该攻克哪M个城堡吗? 思路 简单树形背包dp,当作树形背包的第一道入门题非常合适 题目给的是森林,那么给 增加一个"根节点", 指向森林中的每个树的根节点 f(i, j)表示子树i, 取j个城堡宝藏的时候 的最大值

算法:hdu 1011 Starship Troopers (树形背包dp)

题意 有n个洞穴编号为1-n,洞穴间有通道,形成了一个n-1条边的树, 洞穴的入口即根节点是1. 每个洞穴有x只bugs,并有价值y的金子,全部消灭完一个洞穴的虫子,就可以获得这个洞穴的y个金子. 现 在要派m个战士去找金子,从入口进入.每次只有消灭完当前洞穴的所有虫子,才可以选择进入下一个洞穴. 一个战士可以消灭20只虫子,如果要杀死x只虫子,那么要x/20向上取整即(x+19)/20个战士. 如果要 获得某个洞穴的金子,必须留下足够杀死所有虫子的战士数量, 即(x+19)/20个战士,然后这

算法:hdu 4003 Find Metal Mineral (树形背包dp)

题意 给一棵n个节点的树, 节点编号为1~n, 每条边都有一个花费值. 有k个机器人从S点出发, 问让 机器人遍历所有边,最少花费值多少? 思路 很好的一题, 推荐! 前天看的这题, 今天才想出来的. 方法想出来后,代码很简单 最近做的几道dp,都是一开始没什么想法,然后过两天再想就想出来了,也许是因 为人的潜意识其实会一直在想某个问题 翻看一下网上其他人的做法, 和我的稍有不同, 他们是用f(i, j)表示子树i用j个机器人的最少花费, 一开始我也是这样 去想,但是没想到怎么去状态转移. 然后

算法:poj 2392 Space Elevator(dp 排序+多重背包)

题目大意: 有n种砖头,每种砖头的高为h,数量为c, 且它放的最高位置不能超过a. 问这些砖 最高能够叠多高? 思路: 先把所有种类砖头按照a从大到小排序,然后直接套多重背包即可 . 代码: #include<iostream> #include<queue> #include<stack> #include<cstdio> #include<cstring> #include<cmath> #include<map> #