hdu 4679 Terrorist’s destroy 树形dp水题 (2013多校联合)

本文详细介绍了如何使用树形DP解决一个复杂的树路径问题,即在给定树结构中,切断一条边后,计算出每条边在不同情况下产生的最大值,并最终找到最优解。通过实例演示了从代码逻辑到最终解决方案的全过程,包括状态转移方程的设定、节点信息的维护以及如何处理树结构变化带来的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

4小时55分敲完代码,0调试,跑出样例直接交了,结果爆栈,扩栈交了一发,居然A了,我和我的小伙伴们都惊呆了。。。

题意:给出有n个节点的一棵树,树上的边有权值。我们切断一条边,将整棵树分成两颗,计算一个值,这个值的计算方法,v = b * max ( d1 , d2 ) ;其中,b为所切的边的权值,d1 ,d2 为切断后形成的两颗树的树上的最长路。对于每一条边,会计算出一个v值,问切那条边时,v值最小,如果有多个最小的v值,输出边的id最小的那条。

解题思路:树形dp(对于整棵树而言,根节点为1号节点)。

。对于每一个节点,要记录很多东西,l1 , l2 , l3 分别表示该节点下,最长的,次长的,第三长链的长度,f1 , f2 , f3分别表示最长的,次长的,第三长的链来自于哪个儿子节点。t1 , t2分别表示该节点下的儿子中的最长路和次长路的长度,g1 , g2分别表示最长路,次长路的儿子是哪个儿子。dp[u]表示u节点下的这颗子树的最长路。怎么更新这些信息呢?对于l1 , l2 , l3 , f1 , f2 , f3 枚举每个儿子,递推上来吧,还是很好做的,t1 , t2 就枚举 dp[v] (v表示u的儿子节点)得出来。dp[u]就是max ( t1 , t2 , l1 + l2  ) 。这就可以推出这棵树上每个节点的信息了。然后我么就要算切断某一条边的时候的值了。对于根节点下的边,我们枚举一下,答案还是很容易得出的,d1就是dp[v], d2的话,看枚举的这个v是不是g1,能取t1就取 t1 , 否则就取t2,但这样d2并不一定是最优的,还要从最长,次长,第三长里面取出能去的最长的两条去拼一下,得出一个最大值就是d2了。对于每个节点v,算一次值,更新下答案。然后就要处理以1的儿子为树根了,这是就要把父亲这个节点转变成儿子了,也就是我们要处理的下一个树根多了一个特殊的儿子,就是原来的它的父亲,我们对这个父亲,转变成儿子,要传两个信息下去,它能传下去的最长的l(就是链)的值,和他传下去的最长的t(就是将它变成儿子后,能得到的新的最长路值),而这两个信息,又要根据之前预处理的那些东西进行计算了,链的值就是从能取的最长的链里取一个最长的+1,最长路的值,就是根据能取的儿子的最长路里的值,以及能去的最长的两条边拼出的最大值(这就是为什么要处理三条最长的链了,因为最长的链有可能来自于我要转换成根的那个儿子,而这是不能取的)。转变了树的结构后,我们对于新的根节点,又可以计算它下面的每条边能计算的v值,一直递归,计算出每条边的v值就好了。

代码:

#pragma comment(linker, "/STACK:10600000,10600000")
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100005;
struct Edge {
	int v, next, w;
	Edge(int v, int w, int next) :
			v(v), w(w), next(next) {
	}
	Edge() {
	}
} edge[maxn << 1];

int head[maxn], E, n;
void init() {
	memset(head, -1, sizeof(head));
	E = 0;
}

void add(int s, int t, int w) {
	edge[E] = Edge(t, w, head[s]);
	head[s] = E++;
	edge[E] = Edge(s, w, head[t]);
	head[t] = E++;
}
int v1[maxn], v2[maxn];		//节点
int f1[maxn], f2[maxn];	    //记录是哪个儿子更新的节点信息

int l1[maxn], l2[maxn], l3[maxn]; // 链
int g1[maxn], g2[maxn], g3[maxn];  //记录是哪个儿子更新的链信息
int dp[maxn];
void dfs1(int u, int fa) {
	int i;
	l1[u] = l2[u] = l3[u] = v1[u] = v2[u] = 0;
	dp[u] = 0;
	for (i = head[u]; ~i; i = edge[i].next) {
		int v = edge[i].v;
		if (v == fa)
			continue;
		dfs1(v, u);
		if (v2[u] < dp[v]) {		//节点
			v2[u] = dp[v];
			f2[u] = v;
			if (v1[u] < v2[u]) {
				swap(v1[u], v2[u]);
				swap(f1[u], f2[u]);
			}
		}

		if (l3[u] < l1[v] + 1) {	// 链
			l3[u] = l1[v] + 1;
			g3[u] = v;
			if (l2[u] < l3[u]) {
				swap(l2[u], l3[u]);
				swap(g2[u], g3[u]);
			}
			if (l1[u] < l2[u]) {
				swap(l1[u], l2[u]);
				swap(g1[u], g2[u]);
			}
		}
	}
	dp[u] = max(l1[u] + l2[u], v1[u]);
}
int ans, pos;
int tp[7];
void dfs2(int u, int fa, int fv, int fl) {    // fv节点信息,fl链信息
	int i;
	for (i = head[u]; ~i; i = edge[i].next) {
		int v = edge[i].v;
		if (v == fa) continue;
		//链
		int tt = 0;
		if (g1[u] != v)
			tp[tt++] = l1[u];
		if (g2[u] != v)
			tp[tt++] = l2[u];
		if (g3[u] != v)
			tp[tt++] = l3[u];
		tp[tt++] = fl;
		sort(tp, tp + tt);
		int fll = tp[tt-1]+1;
		//节点
		int d = fv;
		if (f1[u] != v) d = max(d, v1[u]);
		if (f2[u] != v) d = max(d, v2[u]);
		d = max(d, tp[tt - 1] + tp[tt - 2]);
		int fvv = d;


		d = max(d, dp[v]);
		d *= edge[i].w;
		if (ans > d || (ans == d && pos > i)) {
			ans = d;
			pos = i;
		}
		dfs2(v, u, fvv, fll);

	}
}
int x, y, z;
int main() {
	int i, cas;
	scanf("%d", &cas);
	for (int ca = 1; ca <= cas; ca++) {
		scanf("%d", &n);
		init();
		for (i = 0; i < n - 1; i++) {
            scanf("%d%d%d", &x, &y, &z);
			add(x, y, z);
		}
		dfs1(1, -1);
		ans = 1000000009;
		dfs2(1, -1, 0, 0);
		printf("Case #%d: %d\n", ca, (pos>>1)+1);
	}
	return 0;
}






                
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值