计算机问题

问题为hdu2196,运用的算法为动态规划中的树形DP,问题规模较大,枚举不适合,贪心算法又不能最优解,因此用动态规划中的树形DP。

问题描述

前段时间,一所学校买了第一台电脑(所以这台电脑的ID是1)。近年来,学校购买了N-1新电脑。每台新计算机都连接到较早解决的计算机之一。学校的管理人员担心网络功能缓慢,并想知道i-th计算机需要发送信号的最大距离Si(即电缆长度到最远的计算机)。您需要提供此信息。
提示:示例输入与此图相对

应。从图表中,你可以看到计算机 4 离 1 最远,所以 S1 = 3。计算机 4 和 5 是离 2 最远的, 所以 S2 = 2 。计算机 5 是离 3 最远的一个, 所以 S3 = 3 。我们也得到 S4 = 4, S5 = 4 。

输入

输入文件包含多个测试案例。在每种情况下,第一行都有自然编号 N(N<=10000),然后是带有计算机描述的 (N-1) 行。i-th 线包含两个自然数字 - 计算机数量(连接到计算机的计算机数量和用于连接的电缆长度)。电缆总长度不超过 10×9。输入行中的数字由空间分隔。

输出

对于每个案例输出 N 行。i-th 行必须包含 i-th 计算机的 Si 号 (1<=i<=N)。

 首先是对问题分析建立,对任意一点建立子树如图中的2为顶点的电脑,从2出发对他的子树进行DFS广度遍历,记录最大深度,求得L1为最远距离。为了求全部的最远距离需要返回每个节点的最大深度,实际上每个节点都计算了两个距离,即one和two第二长距离。例如2电脑中的距离为电脑5和电脑4。

void dfs1(int father) {
	int one = 0, two = 0;
	for (int i = 0; i <= tree[father].size(); i++) {

		Node child = tree[father][i];
		dfs1(child.id);
		int cost = dp[child.id][0] + child.cost;
		if (cost >= one) {
			two = one;
			one = cost;
		}
		if (cost < one && cost > two)
			two = cost;
	}
	dp[father][0] = two;
	dp[father][1] = two;
}

   当电脑的距离并不如图中所示,也就是分钟更多支线即3和3以上的时候,就需要对其进行另一种解答。将他分成两段,例如2上面还有6,7,8,9这种。将其看成(6,7,8,9)和(2,1,3,4,5)两端。求后一项的时候就要对分别的路径求最长距离L2。甚至是L2中的次长距离two。如果节点刚好在父节点6上最长子树的话,就是two+最长距离。如果不在最长子树上的话,就是one+最长距离。

void dfs2(int father) {
	for (int i = 0; i < tree[father].size(); i++) {
		Node child = tree[father][i];
		if (dp[child.id][0] + child.cost == dp[father][0])
			dp[child.id][2] = max(dp[father][2], dp[father][1]) + child.cost;
		else
			dp[child.id][2] = max(dp[father][2], dp[father][0]) + child.cost;
		dfs2(child.id);
	}
}

综上,求对节点2最远距离就是Max{L1,L2}。用dfs1去实现求解,dfs2实现距离区别和计算。

状态设计:节点i的子树到i的最长距离dp[i][0]以及次长距离dp[i][1];如果是节点i往上还有的话就是最长距离dp[i][2],需要对是否在父节点最长距离上判断。

以下是全局代码:

#include<stdc++.h>
using namespace std;
const int N = 10100;
struct Node{
	int id;
	int cost;
};
vector<Node>tree[N];
int dp[N][3];
int n;
void init_read() {
	for (int i = 1; i <= n; i++)
		tree[i].clear();
	memset(dp, 0, sizeof(dp));
	for (int i = 2; i <= n; i++) {
		int x, y;
		scanf("%d%d", &x, &y);
		Node tmp;
		tmp.cost = y;
		tmp.id = i;
		tree[x].push_back(tmp);
	}
}
void dfs1(int father) {
	int one = 0, two = 0;
	for (int i = 0; i <= tree[father].size(); i++) {

		Node child = tree[father][i];
		dfs1(child.id);
		int cost = dp[child.id][0] + child.cost;
		if (cost >= one) {
			two = one;
			one = cost;
		}
		if (cost < one && cost > two)
			two = cost;
	}
	dp[father][0] = two;
	dp[father][1] = two;
}
void dfs2(int father) {
	for (int i = 0; i < tree[father].size(); i++) {
		Node child = tree[father][i];
		if (dp[child.id][0] + child.cost == dp[father][0])
			dp[child.id][2] = max(dp[father][2], dp[father][1]) + child.cost;
		else
			dp[child.id][2] = max(dp[father][2], dp[father][0]) + child.cost;
		dfs2(child.id);
	}
}
int main() {
	while (~scanf("%d", &n)) {
		init_read();
		dfs1(1);
		dp[1][2] = 0;
		dfs2(1);
		for (int i = 1; i <= n; i++)
			printf("%d\n", max(dp[i][0], dp[i][2]));
	}
	return 0;
}

因为是对每个节点单独做一次深度遍历,所以总复杂度是O(n^2),但由于提莫规模复杂度过大,复杂度只能是O(nlog2n)。dfs1和dfs2都是O(n)。

数媒201wpy

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值