P4408 逃学的小孩-洛谷-java题解-树的直径

传送门:P4408 [NOI2003] 逃学的小孩 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P4408


        题目里千万不要漏了这句话:任两个居住点间有且仅有一条通路!!!!这个条件让我们后面方便求任意一个点到其他点的距离。假设我们已经确定P,Q点为直径端点,那么PQ是必走的,CP,CQ会选取其中小的一段走,所以我们的C点要满足min(CQ,CP)最大,这样就可以使答案最大。min(CP,CQ)最大的意思是,假设我们用两次dfs求得某条直径的两个端点P,Q,那么我们枚举点C,则ans=max(PQ+min(CP,CQ))。直径好求,直接套模板。因为要求端点,所以用两次搜索。然后就是求min(CP,CQ),我们可以稍微改一下树上dp求直径的模板:(下面这个我不好解释,大伙可以自己模拟一组简单的数据,就知道什么意思了)

static void dfs2(int u,int fa){
        List<Edge> edges = tree.get(u);
        for(Edge edge : edges){
            if(fa==edge.v)continue;
            dis[edge.v]=dis[edge.u]+edge.w;
            dfs2(edge.v,u);
        }
    }

原整代码(含解析):

import java.io.*;
import java.util.*;

class Edge{
      int u;
      int v;
      int w;
      public Edge(int u,int v,int w){
          this.u=u;
          this.v=v;
          this.w=w;
      }
}
/*如果有小伙伴对树的直径模板(两次搜索和dp)不熟,建议先去把模板弄熟)*/
class Main {
    static int n,m;
    static long[] dis,disp,disq;
    static boolean[] vis;
    static List<List<Edge>> tree;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer st = new StreamTokenizer(br);
        st.nextToken();n=(int)st.nval;
        st.nextToken();m=(int)st.nval;
        dis=new long[n+1];
        disp=new long[n+1];
        disq=new long[n+1];
        vis=new boolean[n+1];
        tree=new ArrayList<>();
        for(int i=0;i<=n;i++){
            tree.add(new ArrayList<Edge>());
        }
        //以上为数据输入与初始化
        int u,v,w;
        for(int i=1;i<=m;i++){
            st.nextToken();u=(int)st.nval;
            st.nextToken();v=(int)st.nval;
            st.nextToken();w=(int)st.nval;
            tree.get(u).add(new Edge(u,v,w));
            tree.get(v).add(new Edge(v,u,w));
        }
        int P=0,Q=0;        //P,Q为直径两个端点
        long ans=0,max=0;
        vis[1]=true;dfs(1); //vis[1]=true是个好习惯
        for(int i=1;i<=n;i++){
            if(dis[i]>max){
                P=i;
                max=dis[i];
            }
            dis[i]=0;
            vis[i]=false;
        }
        vis[P]=true;dfs(P);//vis[P]是个好习惯
        for(int i=1;i<=n;i++){
            if(dis[i]>ans){
                Q=i;
                ans=dis[i];
            }
            dis[i]=0;
        }
        dis=disp;dfs2(P,0);//求出P点到每一个点的距离
        dis=disq;dfs2(Q,0);
        long anss=0;
        for(int i=1;i<=n;i++){
                if(i==P||i==Q)continue;
                long temp=ans+Math.min(disp[i],disq[i]);
                anss=anss>temp?anss:temp;
        }
        System.out.println(anss);
    }
    static void dfs(int k) {                //标准两次dfs模板
        List<Edge> edges = tree.get(k); 
        int len=edges.size();
        for(int i=0;i<len;i++){
            int to=edges.get(i).v;
            if(!vis[to]){
                vis[to]=true;
                dis[to]=edges.get(i).w+dis[k];
                dfs(to);
            }
        }
    }
    static void dfs2(int u,int fa){        //大家自己模拟一组简单的数据就能明白了
        List<Edge> edges = tree.get(u);
        for(Edge edge : edges){
            if(fa==edge.v)continue;
            dis[edge.v]=dis[edge.u]+edge.w;
            dfs2(edge.v,u);
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
过河卒是一个典型的动态规划问题。首先,我们将整个棋盘看作一个二维数组,数组的每个元素表示到达该位置的路径数目。然后,我们根据题目给出的条件,逐步更新数组中的元素,直到计算出到达目标位置的路径数目。 具体的解题思路如下: 1. 首先,我们可以将马的位置设置为0,表示无法经过该位置。 2. 然后,我们根据马的位置,更新数组中的元素。对于二维数组中的每个位置,我们根据左边和上边的位置来计算到达当前位置的路径数目。具体地,如果左边和上边的位置都可以经过,那么到达当前位置的路径数目就等于左边和上边位置的路径数目之和。如果左边或上边的位置无法经过,那么到达当前位置的路径数目就等于左边或上边位置的路径数目。 3. 最后,我们输出目标位置的路径数目。 下面是洛谷p1002过河卒题解的C++代码: ```cpp #include <bits/stdc++.h> using namespace std; int main() { long long a[21][21]; int x1, y1, x2, y2; cin >> x1 >> y1 >> x2 >> y2; // 初始化数组,马的位置设置为0 for(int i=0; i<=20; i++) { for(int k=0; k<=20; k++) { a[i][k] = 1; } } a[x2][y2] = 0; // 根据马的位置更新数组中的元素 if(x2 >= 2 && y2 >= 1) a[x2-2][y2-1] = 0; if(x2 >= 1 && y2 >= 2) a[x2-1][y2-2] = 0; if(x2 <= 18 && y2 >= 1) a[x2+2][y2-1] = 0; if(x2 <= 19 && y2 >= 2) a[x2+1][y2-2] = 0; if(x2 >= 2) a[x2-2][y2+1] = 0; if(x2 >= 1) a[x2-1][y2+2] = 0; if(y2 >= 1) a[x2+2][y2-1] = 0; if(y2 >= 2) a[x2+1][y2-2] = 0; // 动态规划计算路径数目 for(int i=1; i<=20; i++) { for(int k=1; k<=20; k++) { if(a[i][k] != 0) { a[i][k] = a[i-1][k] + a[i][k-1]; } } } // 输出目标位置的路径数目 cout << a[x1][y1] << endl; return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玛卡左家陇分卡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值