P1099 树网的核
知识点: 树的直径, 模拟, 暴力枚举, 双指针
原题面
题目要求:
给定一棵树 , 边有边权, 并给定一参数 \(S\)
设:
-
\(d(v,F)\) 代表x点到达路径 \(F\)上的点 的 最近的距离
-
偏心距 \(ECC(F)\) 表示距离路径 \(F\) 最远的节点 到路径F的距离
有: \(ECC(F) = \max(d(v,F) , v\in \text{点集})\)
求 : 一段路径 \(F\) , 为树的直径上 长度不超过 \(S\) 的 一段路径
使 偏心距 \(ECC(F)\) 最小化
分析题意:
题目给定了所有的操作方案 及细节 , 直接模拟即可
-
首先 求得树的直径, 并记录树的直径上的点
使用 \(DFS\) 处理较为容易 -
由于数据范围 较小, \(n\le 300\) ,
则可使用 \(Floyd\) 暴力求得 树上任意两点之间 的距离 -
由于数据范围 较小, \(n\le 300\) ,
则可以 再暴力枚举 直径上每一段 长度不超过 \(S\)的 路径-
可以发现 一些性质 :
若固定 路径一端点 , 并向另一侧寻找 合法的端点
若 被固定端点 左移 , 则另一端点 必然不右移两端点的 位置变化量满足单调性
则可以使用 双指针 (尺取法) 来枚举每一条合法路径 -
使用尺取法 枚举 每一段 合法区间
每次都 暴力计算出 所选路径 \(F\) 与路径 \(F\) 外每一个点 的距离
取 最小值作为答案
-
数据加强:
由于 此题数据范围较小, 所以可以直接暴力水过
可以挑战: 数据加强版: \(\text{P2491 [SDOI2011]}\)消防
#include<cstdio>
#include<cstring>
#include<ctype.h>
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
const int MARX = 310;
//=============================================================
int n,s,u,v, ans = 2e9, map[MARX][MARX];
int dis[MARX], pre[MARX];
bool use[MARX];
//=============================================================
inline int read()
{
int s=1, w=0; char ch=getchar();
for(; !isdigit(ch);ch=getchar()) if(ch=='-') s =-1;
for(; isdigit(ch);ch=getchar()) w = w*10+ch-'0';
return s*w;
}
void dfs(int now,int fat,int sum,bool flag)//dfs求得 树的直径
{
if(flag) pre[now] = fat;//第二次dfs记录路径 (前驱
dis[now] = dis[fat] + sum;//更新距离
for(int i = 1; i <= n; i ++)//枚举出边更新
if(map[now][i] <= 1e9 && i!=fat)
dfs(i,now,map[now][i],flag);
}
void get_road()//求得 树的直径
{
dfs(1,0,0,0); //一次dfs
for(int i = 1, maxdis = 0; i <= n; i ++)//选择 距离最远的点
if(dis[i] > maxdis) u = i,maxdis = dis[i];
dfs(u,0,0,1); //二次dfs
for(int i = 1, maxdis = 0; i <= n; i ++)//选择 距离最远的点
if(dis[i] > maxdis) v = i,maxdis = dis[i];
}
void floyd()//floyd 求得 任意两点路径
{
for(int i = 1; i <= n; i ++) map[i][i] = 0;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
for(int k = 1; k <= n; k ++)
if(i != j && j != k)//floyd注意判断
if(map[j][k] > map[j][i] + map[i][k])
map[j][k] = map[j][i] + map[i][k];
}
void solve()
{
int l = v,r = v; use[v] = 1;//初始化
for(int sum = 0; l!=u; use[l]=0, l = pre[l], sum=0)//枚举 左端点
{
memset(dis,63,sizeof(dis));//初始化
for(; map[l][r] <= s;) r = pre[r],use[r] = 1;//更新右端点
use[r] = 0;
for(int i = l; i != r; i = pre[i])//暴力计算出 所选路径 F 与路径F外每一个点 的距离
for(int j = 1; j <= n; j ++)
if(!use[j]) dis[j] = min(dis[j],map[i][j]);
for(int i = 1; i <= n; i ++)//求得 偏心距
if(!use[i]) sum = max(sum,dis[i]);
ans = min(ans,sum);//更新答案
}
}
//=============================================================
signed main()
{
memset(map,63,sizeof(map));//初始化路径长度
n = read(), s = read();
for(int i = 1; i < n ;i ++)
{
int u1 = read(), v1 = read(), w1 = read();
map[u1][v1] = map[v1][u1] = w1;
}
get_road(); floyd(); solve();
printf("%d",ans);
}
作者@Luckyblock,转载请声明出处。