Codeforces1004E-求某条件下的最长链长

(有任何问题欢迎留言或私聊 && 欢迎交流讨论哦

题意: 传送门

 原题目描述在最下面。
 简单来说就是给你一颗n个节点的树,然后你自己选择一条长度不超过k个节点的路径(必须是一条树上的路径)。将这条路径看成一个点,问这个点到树上其他点的最远距离最小是多少?

思路:
法一:

 首先找到树的一条直径。并且处理处直径起点到其他点的距离。
 然后再这个直径上做滑动窗口。滑动窗口的长度不超过k。
 然后在O(n)遍历的时候,dfs找出这个点到其他叶子的最远距离,ans和最远距离取min。注意,这个dfs的过程是到其他叶子的最远距离,若碰到直径上的点就return。

法二:

 用一个set把所有的叶子存起来,整个树也用set存。叶子按权值排序,树按标号排序(方便删除节点)。
 然后维护树上节点数量大于k且叶子数大于2。如果叶子数小于等于2,那图形其实就是一条链。
 然后每次选择权值最小的叶子,把它删掉,同时把这个叶子的权值加到它连接的点上去。如果新产生了叶子,则加入叶子set。
 最后的答案就是叶子权值中最小的那个。

AC代码:

法一:

#include<bits/stdc++.h>
#define mk make_pair
#define pb push_back
#define fi first
#define se second
using namespace std;
typedef long long LL;
const int inf=2e9+1e8+1234;
const LL linf=8e18+9e17;
const int N = 1e5+7;
const int mod = 998244353;
int n, k, ans;
vector<pair<int, int> >mp[N];
int vis[N], dep[N], dis[N], fa[N];
vector<int> diameter;
void dfs1(int u, int Fa){
  if(vis[u])return;
  vis[u] = 1;
  for(int i = 0; i < mp[u].size(); ++i){
    int v = mp[u][i].fi, w = mp[u][i].se;
    if(v == Fa)continue;
    fa[v] = u;
    dis[v] = dis[u] + w;
    dfs1(v, u);
  }
}
void dfs2(int u, int f){
  for(auto x: mp[u]){
    if(vis[x.fi] || x.fi == f)continue;
    dfs2(x.fi, u);
    dep[u] = max(dep[x.fi] + x.se, dep[u]);
  }
}
int main(int argc, char const *argv[]){
  while(~scanf("%d%d", &n, &k)){
    for(int i = 0; i <= n; ++i)mp[i].clear();
    diameter.clear();
    for(int i = 1, u, v, w; i < n; ++i){
      scanf("%d%d%d", &u, &v, &w);
      mp[u].pb(mk(v, w));
      mp[v].pb(mk(u, w));
    }
    int st, ed, tmax = -1;
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    dfs1(1, -1);
    for(int i = 1; i <= n; ++i){
      if(tmax < dis[i]){
        tmax = dis[i];
        st = i;
      }
    }
    memset(fa, 0, sizeof(fa));
    memset(vis, 0, sizeof(vis));
    memset(dis, 0, sizeof(dis));
    dfs1(st, -1);
    tmax = -1;
    for(int i = 1; i <= n; ++i){
      if(tmax < dis[i]){
        tmax = dis[i];
        ed = i;
      }
    }
    memset(vis, 0, sizeof(vis));
    for(int i = ed; i; i = fa[i]){
      diameter.push_back(i);
      vis[i] = 1;
      //printf("%d\n", i);
    }
    ans = inf;
    int h_max = 0, x = 0;
    memset(dep, 0, sizeof(dep));
    for(int i = 0; i < diameter.size(); ++i, x = 0){
      if(i >= k){
        x = dis[ed] - dis[diameter[i - k + 1]];
      }
      x = max(x, dis[diameter[i]]);
      dfs2(diameter[i], -1);
      h_max = max(h_max, dep[diameter[i]]);
      x = max(x, h_max);
      ans = min(ans, x);
    }
    printf("%d\n", ans);
  }
  return 0;
}

法二:

#include<bits/stdc++.h>
#define mk make_pair
#define pb insert
#define fi first
#define se second
using namespace std;
typedef long long LL;
const int inf=2e9+1e8+1234;
const LL linf=8e18+9e17;
const int N = 4e5+7;
const int mod = 998244353;
set<pair<int, int> >mp[N], st;
int n, k, ans;
int du[N];
int main(int argc, char const *argv[]){
  scanf("%d%d", &n, &k);
  for(int i = 1, u, v, w; i < n; ++i){
    scanf("%d%d%d", &u, &v, &w);
    mp[u].pb(mk(v, w));
    mp[v].pb(mk(u, w));
    du[u]++;du[v]++;
  }
  for(int i = 1; i <= n; ++i){
    if(du[i] == 1){
      st.pb(mk((*mp[i].begin()).se, i));
    }
  }
  while(n > k || st.size() > 2){
    --n;
    ans = (*st.begin()).fi;
    int id = (*st.begin()).se;
    st.erase(st.begin());
    int nex = (*mp[id].begin()).fi;
    mp[nex].erase(mp[nex].lower_bound(mk(id, 0)));
    if(mp[nex].size() == 1){
      st.pb(mk((*mp[nex].begin()).se + ans, nex));
    }
  }
  printf("%d\n", ans);
  return 0;
}
/*
10 2
1 2 5
5 7 2
3 2 6
10 6 3
3 8 1
6 4 2
4 1 6
6 9 4
5 2 5

12
*/

原题目描述:

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值