首页 > 试题广场 >

旅游

[编程题]旅游
  • 热度指数:2479 时间限制:C/C++ 3秒,其他语言6秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解
Cwbc和XHRlyb生活在 s 市,这天他们打算一起出去旅游。
旅行地图上有 n 个城市,它们之间通过 n-1 条道路联通。
Cwbc和XHRlyb第一天会在 s 市住宿,并游览与它距离不超过 1 的所有城市,之后的每天会选择一个城市住宿,然后游览与它距离不超过 1 的所有城市。
他们不想住在一个已经浏览过的城市,又想尽可能多的延长旅行时间。
XHRlyb想知道她与Cwbc最多能度过多少天的时光呢?
聪明的你在仔细阅读题目后,一定可以顺利的解决这个问题!

输入描述:
第一行,两个正整数n和s,表示城市个数和第一天住宿的城市s。
接下来n-1行,每行两个整数x和y,表示城市x与城市y之间有一条双向道路,保证无环。


输出描述:
第一行,一个非负整数表示答案。
示例1

输入

4 1
1 2
2 3
3 4

输出

2

说明

第一天,在1号城市住宿,游览了1、2号城市。
第二天,在3号城市住宿,游览了4号城市,旅行结束。

备注:
1 ≤ n ≤ 500000, 1 ≤ s, x, y ≤ n。
建议出题人重学语文,不知哔哔啥,这题不就是没有上司的舞会吗
发表于 2023-08-21 14:41:58 回复(0)
状态DP+树形DP
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 500010, M = 2*N;
int v[M], ne[M], h[N];//用静态链表存用邻接表存储的树(实际存的是无向图)
int f[N][2];//f[i][1]表示在i城市休息时,其子树的最大值,f[i][0]反之
int idx;
//给无向图添加边
void add(int a, int b){
    v[idx]=b, ne[idx]=h[a], h[a]=idx++;
}
//深度搜索,求状态f[i][j]的值
void dfs(int u, int father){
    f[u][0]=0;
    f[u][1]=1;
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = v[i];
        if(j == father) continue;
        dfs(j, u);
        f[u][1]+=f[j][0];
        f[u][0]+=max(f[j][0], f[j][1]);
    }
}
int main() {
    idx=0;
    memset(h, -1, sizeof h);
    int n, root;
    cin >> n >> root;
    for(int i=0; i<n-1; i++){
        int a, b;
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    dfs(root, -1);
    cout << f[root][1] << endl;
    return 0;
}


发表于 2023-02-08 18:00:47 回复(0)
import java.util.*;

public class TreeTravel {
    // 最大节点数(根据题目数据范围调整)
    static final int MAX_N = 500001;
    // DP数组:dp[cur][0]表示不选当前节点的最大天数,dp[cur][1]表示选当前节点的最大天数
    static int[][] dp = new int[MAX_N][2];
    // 邻接表存储树结构 - 使用列表的列表替代泛型数组
    static List<List<Integer>> G = new ArrayList<>();

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int s = scanner.nextInt();

        // 初始化邻接表
        for (int i = 0; i <= n; i++) {
            G.add(new ArrayList<>()); // 每个节点对应一个邻接表
        }

        // 初始化DP数组:每个节点选自己时默认贡献1天(初始住宿天数)
        for (int i = 1; i <= n; i++) {
            dp[i][1] = 1; // 选当前节点时,至少有1天住宿(自己)
        }

        // 读取边并构建树
        for (int i = 1; i < n; i++) {
            int u = scanner.nextInt();
            int v = scanner.nextInt();
            G.get(u).add(v); // 无向树,双向添加
            G.get(v).add(u);
        }

        // 从起点s开始深度优先搜索,父节点初始为-1(无父节点)
        dfs(s, -1);

        // 输出结果:选起点s时的最大天数(第一天必须选s)
        System.out.println(dp[s][1]);
        scanner.close();
    }

    /**
     * 深度优先搜索计算DP值
     * @param cur 当前节点
     * @param pre 当前节点的父节点(避免回溯)
     */
    private static void dfs(int cur, int pre) {
        // 遍历当前节点的所有邻接节点
        for (int next : G.get(cur)) {
            if (next == pre) { // 跳过父节点,避免循环
                continue;
            }
            dfs(next, cur); // 递归处理子节点

            // 状态转移:不选当前节点cur时,子节点可以选或不选,取最大值
            dp[cur][0] += Math.max(dp[next][1], dp[next][0]);
            // 状态转移:选当前节点cur时,子节点不能选(只能取子节点不选的情况)
            dp[cur][1] += dp[next][0];
        }
    }
}

发表于 2025-06-11 11:29:12 回复(0)
     * 计算:最大游玩天数/最大住宿天数(头天住宿次日游玩,所以住宿天数=游玩天数)
     *  【等价问题】计算:最大不相邻城市节点数 / 求树上最多的不相邻的点的问题(最大独立集)
     *  【等价问题】因为要求:住宿城市节点不能相邻/不能住在已游览的城市/不能住在前次住宿城市的所有相邻城市节点
     *
     * 算法一:树形DP/动态规划DP/自底向上
     *  【树形DP vs 线性DP】指父问题/子问题对应的输入数据结构为树形结构,基于树形结构数据的DP称为树形DP。
     *
     * 解题思路:
     *  (1)首先在地图上有 n 个城市,一共有n-1条边/道路,可以看作一棵树;
     *      题目指定了第一天住宿的城市s,则可以看成是以“城市s”为根节点的一棵树;    s=firstCityNo
     *  (2)然后,每天会选择一个城市住宿,然后游览与它距离不超过 1 的所有城市,
     *      相当于住宿在树的节点x时,游览“当前节点、父节点、所有子节点”,也即与住宿城市直接相连的所有城市节点;
     *  (3)再就是,他们不想住在一个已经游览过的城市;
     *      也就是说:选择住/不住某个城市x,直接影响游览的城市节点;
     *              进一步影响其他城市是否可以住宿,最终对“最大游玩天数/最大住宿天数”有影响。
     *      因此,除第一个住宿城市外,每个城市都要考虑2种情况:住/不住时,最大游玩天数/最大住宿天数。
     *
     *【降维关系/计算规则/状态转移方程】推理:
     *  (1)因为“住宿城市节点不能相邻/不能住在已游览的城市”,
     *      因此,选择住/不住某个城市x,对在以“城市x”为根节点的城市子树上的“最大游玩天数/最大住宿天数”有影响;
     *      所以,在以“城市x”为根节点的城市子树上,每个城市节点都考虑“住/不住”2种情况。
     *  (2)则定义子问题:
     *       g(x) = 在以“城市x”为根节点的城市子树上,最大游玩天数/最大住宿天数;
     *            = max{h(x,0), h(x,1)}        x:[1,n]
     *          其中:子问题h(x,z):在以“城市x”为根节点的城市子树上,住/不住城市x,最大游玩天数/最大住宿天数;
     *      则原问题f(n) 表示为:
     *       f(n) = 在指定了第一天住宿的城市s时,最大游玩天数/最大住宿天数;
     *            = h(s,1);
     *
     *  (3)再看子问题h(x,z)怎么求解? x:[1,n],z:[0,1]
     *      子问题h(x,z):在以“城市x”为根节点的城市子树上,住/不住城市x,最大游玩天数/最大住宿天数;
     *      情形一:“城市x”节点,无子城市节点:
     *           h(x,z) = h(x,0) = 0;      条件:z==0  不住城市x
     *           h(x,z) = h(x,1) = 1;      条件:z==1  住城市x
     *      情形二:“城市x”节点,有子城市节点y1,……,ym:
     *           h(x,z) = h(x,0) = SUM{ g(y1), …… ,g(ym)};              条件:z==0
     *                           = SUM{ max{h(y1,0), h(y1,1)}, …… , max{h(ym,0), h(ym,1)}}
     *           h(x,z) = h(x,1) = 1 + SUM{ h(y1,0), …… ,h(ym,0)};      条件:z==1
     *
     *
     */
    public static void main(String[] args) throws Exception {
        //Scanner in = new Scanner(System.in);
        //高性能输入
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

        //输入数据
        //1.第一行,两个正整数n和s,表示城市个数和第一天住宿的城市s。
        String[] param = br.readLine().split(" ");
        int n = Integer.valueOf(param[0]);
        int s = Integer.valueOf(param[1]);
        int firstCityNo =s;
       
        //2.接下来n-1行,每行两个整数x和y,表示城市x与城市y之间有一条双向道路。
        int[][] cityLines = new int[n-1][];
        int index=0;
        int q=n-1;
        while(q-->0){
            cityLines[index++] = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::valueOf).toArray();
        }

        //3.计算:最大游玩天数/最大住宿天数(头天住宿次日游玩,所以住宿天数=游玩天数)
        //【等价问题】计算:最大不相邻城市节点数 / 求树上最多的不相邻的点的问题(最大独立集)
        //【等价问题】因为要求:住宿城市节点不能相邻/不能住在已游览的城市/不能住在前次住宿城市的所有相邻城市节点
       
        //算法一:树形DP/动态规划DP/自底向上
        // 【树形DP vs 线性DP】指父问题/子问题对应的输入数据结构为树形结构,基于树形结构数据的DP称为树形DP。
        int maxPlayDays = maxPlayDays(n, firstCityNo, cityLines);

        //5. 返回结果
        //最大游玩天数/最大住宿天数
        System.out.println(maxPlayDays);
    }

    public static int maxPlayDays(int n, int firstCityNo, int[][] cityLines) {
        //输入检查
        if(n<=0 || null==cityLines){
            return 0;
        }
        if(n<=2){
            return 1;
        }

        //算法一:树形DP/动态规划DP/自底向上
        //  【树形DP vs 线性DP】指父问题/子问题对应的输入数据结构为树形结构,基于树形结构数据的DP称为树形DP。
       
        //0.数据准备/预处理
        Map<Integer,List<Integer>> cityTrees = new HashMap<>();
        //初始化 cityTrees
        for(int i=1;i<=n;i++){
            cityTrees.put(i,new ArrayList<Integer>());
        }
        //构建 cityTrees
        for(int i=0;i<cityLines.length;i++){
            cityTrees.get(cityLines[i][0]).add(cityLines[i][1]);
            cityTrees.get(cityLines[i][1]).add(cityLines[i][0]);
        }

        //1.定义 中间结果集矩阵dp[n][2]
        //dp[x-1][z]:保存子问题h(x,z)的解:在以“城市x”为根节点的城市子树上,住/不住城市x,最大游玩天数/最大住宿天数;
        int[][] dp = new int[n][2];

        //2.计算 中间结果集矩阵dp:子问题h(x,z)
        maxPlayDays(firstCityNo, 0, cityTrees, dp);

        //3. 返回结果
        //最大游玩天数/最大住宿天数 = f(n) = h(s,1);
        return dp[firstCityNo-1][1];
    }


    /**
     * 计算:子问题h(x,z)的解:在以“城市x”为根节点的城市子树上,住/不住城市x,最大游玩天数/最大住宿天数;
     *
     *
     */
    public static void maxPlayDays(int x, int parent, Map<Integer,List<Integer>> cityTrees, int[][] dp) {
        //数据准备
        //“城市x”节点,其子城市节点:
        List<Integer> linkcitys = cityTrees.get(x);
        linkcitys.remove((Integer)parent);       //remove有2个版本:指定元素(对象类型)/索引(int类型)


        //1.额外行列初始化
        //无

        //2.特殊处理/边界计算/无法降维子问题计算
        //情形一:“城市x”节点,无子城市节点:
        if(linkcitys.size()==0){
            //h(x,z) = h(x,0) = 0;      条件:z==0  不住城市x
            dp[x-1][0] = 0;
            //h(x,z) = h(x,1) = 1;      条件:z==1  住城市x
            dp[x-1][1] = 1;
        }

        //3.统一降维计算
        //降维关系:如前。
        //情形二:“城市x”节点,有子城市节点y1,……,ym:
        else {
            //31.先递归计算子问题的解,并保存到中间结果集矩阵dp。
            for(Integer childCityNo:linkcitys){
                maxPlayDays(childCityNo, x, cityTrees, dp);
            }

            //32.计算父问题,复用子问题解
            //h(x,z) = h(x,0) = SUM{ g(y1), …… ,g(ym)};              条件:z==0
            //                = SUM{ max{h(y1,0), h(y1,1)}, …… , max{h(ym,0), h(ym,1)}}
            //h(x,z) = h(x,1) = 1 + SUM{ h(y1,0), …… ,h(ym,0)};      条件:z==1
            dp[x-1][0] = 0;
            dp[x-1][1] = 1;
            for(Integer childCityNo:linkcitys){
                dp[x-1][0] += Math.max(dp[childCityNo-1][0], dp[childCityNo-1][1]);
                dp[x-1][1] += dp[childCityNo-1][0];
            }
        }
    }
    
发表于 2025-03-25 07:39:55 回复(0)
这题python是不是没法ac,显示递归RecursionError: maximum recursion depth exceeded in comparison

import sys
from collections import defaultdict
n, s = list(map(int, input().strip().split(' ')))

neighbours = defaultdict(list)

for i in range(n-1):
    u, v = list(map(int, input().strip().split(' ')))
    neighbours[u].append(v)
    neighbours[v].append(u)

dp = [[0]*2 for _ in range(n+1)]

def dfs(root,pre):
    # state: 0 = not stay, 1 = stay
    next_nodes = neighbours[root]
    for next_node in next_nodes:
        if next_node != pre: # 防止死递归
            dfs(next_node,root)
            dp[root][1] += dp[next_node][0]
            dp[root][0] += max(dp[next_node][0],dp[next_node][1])
    dp[root][1] += 1

dfs(s,-1)
print(dp[s][1])



发表于 2024-10-27 07:43:48 回复(0)
#include <iostream>
#include <cstring>
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 500010, M = 2 * N;
int h[N], e[M], ne[M], idx;
bool st[N];

PII dfs(int root){
    st[root] = true;
    int s = 1, t = 0;
    for(int i = h[root]; ~i; i = ne[i]){
        int u = e[i];
        if(st[u]) continue;
        PII p = dfs(u);
        s = max(s, s + p.y);
        t = max(t, max(t + p.x, t + p.y));
    }
    return {s, t};
}

void add(int a, int b){
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
int main() {
    memset(h, -1, sizeof h);
    int n, root;
    cin >> n >> root;
    int a, b;
    for(int i = 0; i < n - 1; i++){
        cin >> a >> b;
        add(a, b);
        add(b, a);
    }
    PII ans = dfs(root);
    cout << ans.x;
    return 0;
}
    
// 64 位输出请用 printf("%lld")


发表于 2023-01-10 18:07:27 回复(0)
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int n = in.nextInt();
            int s = in.nextInt();
            HashMap<Integer, ArrayList<Integer>> map = new HashMap<>();
            for (int i = 0; i < n - 1; i++) {
                int x = in.nextInt();
                int y = in.nextInt();
                if (map.get(x) == null) {
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(y);
                    map.put(x, list);
                } else map.get(x).add(y);

                if (map.get(y) == null) {
                    ArrayList<Integer> list = new ArrayList<>();
                    list.add(x);
                    map.put(y, list);
                } else map.get(y).add(x);
            }
            int[][] dp = new int[n + 1][2];
            dfs(map, dp, s, -1);
            //System.out.println(Math.max(dp[s][0], dp[s][1])); 看清题目是s点放入 所以只有一种情况
            System.out.println( dp[s][1]);
        }
    }
    public static void dfs(HashMap<Integer, ArrayList<Integer>> map, int[][] dp,
                           int root, int pre) {
        dp[root][1] = 1;
        //root 当前结点 pre 父节点
        if (map.get(root) == null) {
            return; //叶结点
        }
        for (int i = 0; i < map.get(root).size(); i++) {
            int v = map.get(root).get(i); //子节点
            if (v == pre) continue; //找回到了根结点
            dfs(map, dp, v, root);
            dp[root][1] += dp[v][0];
            dp[root][0] += Math.max(dp[v][1], dp[v][0]);
        }
    }
}

发表于 2022-09-19 11:45:33 回复(0)
极限爆内存,再多2KB就超过64MB了
#include<iostream>
#include<vector>
#include<queue>
using namespace std;

vector<int> edge[500009];
vector<int> childs[500009];
bool p[500009];
int dpnone(int);
int dphave(int);

void bfs_tree(int start){
  queue<int> bfs;
  bfs.push(start);
  while(!bfs.empty()){
    int curr=bfs.front();
    for(auto node:edge[curr]){
      if(p[node]==0){
        p[node]=1;
        childs[curr].push_back(node);
        bfs.push(node);
      }
    }
    bfs.pop();
  }
}

int main(){
  int n,s;
  cin>>n>>s;
  p[s]=1;
  for(int i=0;i<n-1;i++){
    int a,b;
    cin>>a>>b;
    edge[a].push_back(b);
    edge[b].push_back(a);
  }
  bfs_tree(s);
  cout<<dphave(s)<<endl;
}

int dpnone(int node){
  static int cache[500009];
  if(cache[node]) return cache[node];
  int result=0;
  for(int child:childs[node]){
    result+=max(dphave(child),dpnone(child));
  }
  return cache[node]=result;
}

int dphave(int node){
  static int cache[500009];
  if(cache[node]) return cache[node];
  int result=1;
  for(int child:childs[node]){
    result+=dpnone(child);
  }
  return cache[node]=result;
}


发表于 2022-09-03 23:59:06 回复(0)
#include<bits/stdc++.h>
usingnamespacestd;
constintMAX_N=500000 + 5;
intn,s,f[MAX_N][2];
intLast[MAX_N],Next[MAX_N<<1],End[MAX_N<<1],tot;
voidaddedge(intx,inty){
    End[++tot]=y;
    Next[tot]=Last[x];
    Last[x]=tot;
}
voidsolve(intx,intfa){
    for(inti=Last[x];i;i=Next[i]){
        inty=End[i];
        if(y!=fa){
            solve(y,x);;
            f[x][1]+=f[y][0];
            f[x][0]+=max(f[y][0],f[y][1]);
        }
    }
    f[x][1]++;
}
intmain(){
    scanf("%d%d",&n,&s);
    for(inti=1;i<n;i++){
        intx,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
        addedge(y,x);
    }
    solve(s,0);
    printf("%d\n",f[s][1]);
    return0;
}
发表于 2022-07-11 14:53:15 回复(0)

cpp的vector在容量较大时,如果size == capacity需要分配更多内存,再次分配的空间是当前内存乘2,那个50w的用例可能一下次会分配太多😅

发表于 2021-12-20 16:43:50 回复(0)
python 第11个用例超时,不知道为什么,,
def ans():
    num, start = map(int, input().split(" "))
    tree = {start:[]}
    for _ in range(num-1):
        t1, t2 = map(int, input().split(" "))
        if t1 in tree:
            tree[t1].append(t2)
        else:
            tree[t1] = [t2]
        if t2 in tree:
            tree[t2].append(t1)
        else:
            tree[t2] = [t1]
    dp = [[0,1] for _ in range(num+1)]
    def dfs(cur, pre):
        for next in tree[cur]:
            if next == pre:
                continue
            dfs(next,cur)
            dp[cur][1] += dp[next][0]
            dp[cur][0] += max(dp[next][0], dp[next][1])
    dfs(start,-1)
    print(dp[start][1])
ans()


发表于 2021-11-04 11:29:06 回复(0)
      1
  2     3
4  5  6  7

这种树 从1开始 是不是应该无解
第一天 1 2 3 选2或3住下
第二天 2 4 5 或者 3 6 7  这时候不能选2住下吧
还是我题目理解错了
发表于 2021-10-20 15:25:13 回复(1)