1. 程式人生 > 實用技巧 >P1099 樹網的核

P1099 樹網的核

連結:Miku


這裡是O(\(n^2\))的做法

首先可以證明,對於每一條直徑,求出的偏心距是一樣的

怎麼證明?顯然(我不會)

怎樣求樹的直徑?簡單。

貪心:在一條直徑上,顯然選擇的路徑越長越好


實現:首先求出樹上所有點之間的距離(\(n^2\))一直dfs就行

然後找出直徑及直徑經過的點

最後在直徑上貪心的取即可

(當年的資料太water了)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int head[400];
struct b{
	int to;
	int ne;
	int v;
}e[1000];
int p;
int n,s;
int x,y,z;
int dis[400][400];
int l,r; 
int q[400];
int vis[400];
int fl;
void add(int f,int t,int v){
	p++;
	e[p].v=v;
	e[p].to=t;
	e[p].ne=head[f];
	head[f]=p;
	return ;
}
void dfs(int f,int now){
	for(int i=head[now];i;i=e[i].ne){
		if(!vis[e[i].to]){
			vis[e[i].to]=1;
			dis[f][e[i].to]=dis[f][now]+e[i].v;
			dfs(f,e[i].to);
		}
	}
}
void dfs2(int now){//把直徑記錄下來 
	if(vis[now]||fl)
	return ;
	vis[now]=1;
	if(now==r){
		fl=1;
		return ;
	}
	for(int i=head[now];i;i=e[i].ne){
		q[++p]=e[i].to; 
		dfs2(e[i].to);
		if(fl)
		return ;
		p--;
	}
	return ;
}
int find(){//更新 
	int ans=0;
	for(int i=1;i<=n;++i){
		if(vis[i])
		continue;
		int li=0x7ffff;
		for(int j=1;j<=n;++j){
			if(vis[j])
			li=min(li,dis[i][j]);
		}
		ans=max(li,ans);
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&s);
	for(int i=1;i<n;++i){
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z);
		add(y,x,z); 
	}
	for(int i=1;i<=n;++i){
		memset(vis,0,sizeof(vis));
		vis[i]=1;
		dfs(i,i);
	}
	int li=0;
	for(int i=1;i<=n;++i){
		if(dis[1][i]>li){
			li=dis[1][i];
			l=i;
		}
	}
	li=0;
	for(int i=1;i<=n;++i){
		if(dis[l][i]>li){
			li=dis[l][i];
			r=i;
		}
	}
	memset(vis,0,sizeof(vis)) ;
	p=0;
	q[++p]=l;
	dfs2(l);
	int ans=324;
	int sum=0;
	for(int i=1;i<=p;++i){
		li=0;
		sum=0;
		memset(vis,0,sizeof(vis));
		int j;
		for( j=i;j<=p&&dis[q[i]][q[j]]<=s;++j){
			vis[q[j]]=1;
		}//貪心解決 
		ans=min(ans,find()); 
	}
	cout<<ans;
	return 0;
}