题解 P1099 【树网的核】
钱逸凡
2018-06-21 13:39:47
~~这么简单的题我竟然花了几个小时~~
# 看到数据规模就知道可以枚举暴力
方法:
先预处理任意两点距离。
再枚举可以成为树网的核的路径。
偏心距计算方法:
```
inline int find_f(int i,int j){
if(vis[i][j]!=0)return vis[i][j];//防止同一段算了很多次
int ma=0;
for(int k=1;k<=n;k++){
ma=max(ma,(dist[i][k]+dist[j][k]-dist[i][j])/2);
}
return vis[i][j]=vis[j][i]=ma;
}//以i,j为树网的核,找偏心距
```
C++AC代码
```
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
inline int Read(){
int x=0;
char c=getchar();
while(c>'9'||c<'0')c=getchar();
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return x;
}//读入优化
struct Node{
int v;
int next;
int val;
}node[2020];
int dist[2020][2020];
int maxn;
int vis[2010][2010],df[2010][2010];
int n,s,f;
queue<int>q;
int inque[1010];
int top,head[1010];
inline void addedge(int u,int v,int val){
node[++top].v=v;
node[top].val=val;
node[top].next=head[u];
head[u]=top;
}//邻接表加边
inline int max(int a,int b){
return a>b?a:b;
}
inline int min(int a,int b){
return a<b?a:b;
}
void spfa(int st){
dist[st][st]=0;
q.push(st);
int u,i,d;
while(!q.empty()){
u=q.front();
inque[u]=0;
q.pop();
for(i=head[u];i;i=node[i].next){
d=node[i].v;
if(dist[st][d]>dist[st][u]+node[i].val){
dist[st][d]=node[i].val+dist[st][u];
maxn=max(maxn,dist[st][d]);
if(inque[d]==0){
q.push(d);
inque[d]=1;
}
}
}
}
}//spfa
inline int find_f(int i,int j){
if(vis[i][j]!=0)return vis[i][j];
int ma=0;
for(int k=1;k<=n;k++){
ma=max(ma,(dist[i][k]+dist[j][k]-dist[i][j])/2);
}
return vis[i][j]=vis[j][i]=ma;
}//以i,j为树网的核,找偏心距
int dfs(int i,int j){
if(df[i][j]!=0)return df[i][j];//记忆化省时间
int mi=1<<30,k,d;
mi=min(mi,find_f(i,i));//树网的核可以为单个点
mi=min(mi,find_f(j,j));
if(dist[i][j]<=s)mi=min(mi,find_f(i,j));//线段(i,j)可以成为树网的核
for(k=head[i];k;k=node[k].next){
d=node[k].v;
if(dist[d][j]+dist[i][d]==dist[i][j]){//d在直径上
if(dist[d][j]<=s)mi=min(mi,find_f(d,j));//线段(d,j)可以为树网的核
mi=min(mi,dfs(d,j));
break;//肯定只有一个与i相连的点在这条直径上
}
}//从i向j找树网的核
for(k=head[j];k;k=node[k].next){
d=node[k].v;
if(dist[d][j]+dist[i][d]==dist[i][j]){
if(dist[i][d]<=s)mi=min(mi,find_f(i,d));
mi=min(mi,dfs(i,d));
break;
}
}//同上,从j向i走
return df[i][j]=df[j][i]=mi;
}//i,j表示直径两端点
int main(){
memset(dist,0x3f3f3f3f,sizeof(dist));
n=Read(),s=Read();
f=1<<30;
register int i,j;
int a,b,val;
for(i=1;i<n;i++){
a=Read(),b=Read(),val=Read();
addedge(a,b,val);
addedge(b,a,val);
}
for(i=1;i<=n;i++)spfa(i);//得到任意两点距离
for(i=1;i<=n;i++){
for(j=i+1;j<=n;j++){
if(dist[i][j]==maxn){//找直径
f=min(f,dfs(i,j));//求最小偏心距
}
}
}
printf("%d",f);
return 0;
}
```