【解题报告】HDU 4679 Terrorist’s destroy -- 树形dp 删一边求两子树直径

/*
	HDU 4679 Terrorist’s destroy 
	给一棵树,任意删一条边,树分成了两个部分(a,b),求min( max(a.直径,b.直径) * v.w ) 
	最大直径乘以边权 积的最小值
	解法:
	先找出整棵树的直径所在,即两个端点ds de
	然后保存每个点到ds和de之间的距离 dds[] dde[]
	然后从ds(de)开始搜
		每个点保存子树中到de(ds)之间距离的最大值mde[](mds[]),这里要用到dds[]和dde[]
	然后枚举删边,从ds(或de)开始搜
	当删father,child之间的一条边的时候,分成了两个子树,我们的max(mde[father],mds[child])就是两个子树的最大直径
	然后就没有然后了。。。。
	几乎每一部操作都要搜两次

*/
#pragma comment(linker, "/STACK:102400000,102400000")
#include <functional>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <string>
#include <vector>
#include <ctime>
#include <queue>
#include <cmath>
#include <set>
#define CLR(a,v) memset(a,v,sizeof(a))
using namespace std;
typedef long long       ll;
typedef pair<int,int>   pii;

const int INF = 1<<29;
const int N = 1e5+10;

struct node{
	int hid , w , pid; // houseid wight pathid
	node(){}
	node(int a,int b,int v):hid(a),w(b),pid(v){}
};

vector <node > m[N];

int ds,de; // 直径的端点
int dds[N],dde[N]; // 以ds、de为根深度
int mds[N],mde[N]; // 以ds(de)为根每个节点存其子树节点到de(ds)的最远距离

void dfs(int now,int fa,int& dtmp,int ddtmp[]){ // 求端点
	if(m[now].size()==1 && m[now][0].hid==fa && ddtmp[now] > ddtmp[dtmp]){
		// 如果是叶子
		dtmp = now;return;
	}
	int child;
	for(int i = 0 ; i < m[now].size() ; i++){
		if( (child=m[now][i].hid) == fa)continue;
		ddtmp[child] = ddtmp[now] + 1;
		dfs(child,now,dtmp,ddtmp);
	}
}

void dfs(int now,int fa,int mdtmp[],int ddtmp[]){ // 求最远距离
	int child ;
	mdtmp[now]  = ddtmp[now];
	for(int i = 0 ; i < m[now].size() ; i++){
		if( (child=m[now][i].hid) == fa)continue;
		dfs(child ,now , mdtmp,ddtmp);
		mdtmp[now] = max(mdtmp[now],mdtmp[child]);
	}
}

int ans , ansid;
void dfs(int now,int fa){ // 枚举删边,删完一条边后分成了两段
	int child;
	for(int i = 0; i < m[now].size() ; i++){
		if( (child=m[now][i].hid) == fa )continue;
		//判断是不是直径(判断两段的最长链相加等于直径长)dds[de]就是直径长
		//int w = m[now][i].w * ( (dds[now] + dde[child] +1 == dds[de]) ? (max(mds[now] , mde[child])) : (dds[de]) );
		int w = m[now][i].w * max(mds[now] , mde[child]);
		if(w < ans){
			ans = w;
			ansid = m[now][i].pid;
		}else if(w == ans && m[now][i].pid < ansid)
			ansid = m[now][i].pid;
		dfs(child,now);
	}
}

int main(){
	//freopen("in","r",stdin);
	//freopen("out.txt","w",stdout);
	
	// g++ 扩栈
	//int size = 20 << 20; // 20MB  
	//char *p = (char*)malloc(size) + size;  
	//__asm__("movl %0, %%esp\n" :: "r"(p));
	
	int T,n,trash=0,ca=1;cin >> T;
	while(T--){
		scanf("%d",&n);
		for(int i = 1 ; i < n ; i++){
			int a,b,v;
			scanf("%d%d%d" , &a , &b , &v);
			node c1(b,v,i),c2(a,v,i);
			m[a].push_back(c1);
			m[b].push_back(c2);
		}
		ds = de = 0;
		CLR(dds,0);dds[1]=1;
		dfs(1 , 0 , ds , dds); // only find ds
		
		CLR(dde,0);CLR(dds,0);dde[ds]=1;
		dfs(ds , 0 , de , dds); // find de and calc dds[]
		dfs(de , 0 , trash , dde); // only calc dde[]

		CLR(mds,0);mds[de]=0;
		dfs(de,0,mds,dds);
		CLR(mde,0);mde[ds]=0;
		dfs(ds,0,mde,dde);
		
		ans = ansid = INF;
		dfs(ds,0);
		printf("Case #%d: %d\n",ca++,ansid);
		for(int i =  0 ; i <= n ; i++)
			m[i].clear();
	}
	return 0;
}

你可能感兴趣的:(【解题报告】HDU 4679 Terrorist’s destroy -- 树形dp 删一边求两子树直径)