A - Terrorist’s destroy HDU - 4679 (切边)

给你一棵树,每两点的之间距离都是1,毁掉每条边都有对应的消耗w,毁掉一条边后把这棵树变成两棵树,两颗子树中较大的树的直径为L,求w*L的最小值,输出毁掉的边的ID

最直接的做法,硬暴力,for边,得到两颗子树,一边求一次直径,O(n*2n),理论上应该过不了,不过队友过了,那就是应该可以过。(没过就是写的太丑了)

还有树上dp的解法,不懂啥是树上dp。可以找别的博客看。

 

分析题目:

首先求出整棵树直径,一个点记录为起点,另一个为终点,那么再for边时,就分两种情况,

第一:边在直径上,那么ans=e[i].w*max(mx_s[u],mx_t[v]);//mx_s[u],以u为断点,其中左边子树的直径,mx_s[u]同理

第二:边不在直径上,ans=e[i].w*len;

判断是否在直径上可以标记一遍,也可以直接判断dis_st[u]+dis_en[v]+1==len;

剩下的问题就是维护mx_s和mx_t数组。

用了一个getmax函数维护。大致想法就是st-->u已经是这颗子树直径的一段,那么另一段就是要在u的儿子中找一个离st最远的点,这个值就是mx_s[u]。

 

#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <string>
#define MAXN 100005
#define LL long long
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
int n, m;
int st,en;
struct Edge
{
	int next, to, w, id;
}e[MAXN * 2];
int res = INF;
int id = 0;
int head[MAXN], cnt, len;
void init() {
	res = INF;
	memset(head, 0, sizeof(head));
	cnt = 0;
	len = 0;
	id = 0;
}

void add_edge(int a, int b, int w, int id)
{
	e[cnt].id = id;
	e[cnt].to = b;
	e[cnt].w = w;
	e[cnt].next = head[a];
	head[a] = cnt++;
}

int dis_st[MAXN], dis_en[MAXN], mx_s[MAXN], mx_e[MAXN];

void dfs(int u, int fa, int dis[])
{
	for (int i = head[u]; i != -1; i = e[i].next)
	{
		int v = e[i].to;
		if (v != fa)
		{
			dis[v] = dis[u] + 1; dfs(v, u, dis);
		}
	}
}





void getmax(int u, int fa, int mx[], int dis[]) {
	mx[u] = dis[u];
	for (int i = head[u]; i != -1; i = e[i].next) {
		int v = e[i].to;
		if (v != fa) {
			getmax(v, u, mx, dis);
			mx[u] = max(mx[u], mx[v]);
		}
	}
}

void dfs1(int u,int fa) {
	for (int i = head[u]; i != -1; i = e[i].next)
	{
		int v = e[i].to;
		if (v != fa)
		{
			int w = 0;
			if (dis_st[u] + 1 + dis_en[v] == len)w = e[i].w*max(mx_s[u], mx_e[v]);
			else w = e[i].w*len;
			if (w<res) { res = w; id = e[i].id; }
			else if (w == res) id = min(id, e[i].id);
			dfs1(v, u);
		}
	}

}

int main() {
	int T;
	scanf("%d", &T);
	int c = 0;
	while (T--) {

		cnt = 0;
		memset(head, -1, sizeof(head));
		scanf("%d", &n);
		for (int i = 1; i < n; i++) {
			int u, v, w;
			scanf("%d%d%d", &u, &v, &w);
			add_edge(u, v, w, i);
			add_edge(v, u, w, i);
		}
		st = 1;
		dis_st[1] = 0;
		dfs(1, -1, dis_st);
		for (int i = 1; i <= n; i++)if (dis_st[st]<dis_st[i])st = i;
		dis_st[st] = 0;
		en = 1;
		dfs(st, -1, dis_st);
		len = dis_st[1];
		for (int i = 1; i <= n; i++)if (dis_st[en]<dis_st[i]) { en = i; len = dis_st[en]; }
		dis_en[en] = 0;
		dfs(en, -1, dis_en);
		getmax(st, -1, mx_e, dis_en);
		getmax(en, -1, mx_s, dis_st);
		res = INF;
		dfs1(st, -1);
		
		printf("Case #%d: %d\n", ++c, id);
	}

	return 0;
}

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值