美团算法笔试编程题第一题有思路嘛
发布于 2018-09-06 21:48 3400 次浏览 0 赞 来自 我要提问  

RT,


说给了一个生成树,恰好N个点N-1条边,每条边权重相同都是1,如何从固定点出发遍历,可以经过每个点至少一次。终点可以是任意的。


好像旅行商问题可以解,但是复杂度太高,而且旅行商要求回到原点。


输入的N可以到10^5,完全没思路,好方

16 条回复

要求是找最短路径... 忘记写了

2018-09-06 21:52

找最大深度吧 然后2*(N-1)减掉就行了

2018-09-06 21:53
3

因为每个节点遍历后都会判断是否往回走,不往回走的情况就是在深度最大的情况下,把树中的每个边都走两遍即2*(n-1),然后求树的最大深度deep,答案就是2*(n-1)-deep

2018-09-06 21:54
8

妙啊! 感谢提醒思路...

2018-09-06 22:00
1
coder_JB4V922C 回复 coder_IVOE62Pt

大佬能贴个代码吗?我编程实现能力太差,想看看大佬的实现过程

2018-09-06 22:16
coder_YFWRRD62 回复 coder_IVOE62Pt

膜拜大神。

2018-09-06 22:22

1节点为树的根节点,然后找最大深度deep,我使用dp找的

然后2(n-1)-deep,AC了

2018-09-06 22:26
2
coder_MW4CHDQW 回复 coder_IVOE62Pt

大神~~

2018-09-07 09:58
import java.util.LinkedList;
import java.util.List;

public class Main1 {

    static class Node {
        int data;
        List<Node> list = new LinkedList<>();

        public Node(int data) {
            this.data = data;
        }
    }

    public static void main(String[] args) {
        int n = 4;
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);

        n1.list.add(n2); n1.list.add(n3);
        n3.list.add(n4);

        System.out.println(2 * (n - 1) - height(n1));
    }

    private static int height(Node root) {
        LinkedList<Node> list = new LinkedList<>();
        list.add(root);
        int height = -1;

        while (!list.isEmpty()) {
            height ++;
            int size = list.size();
            for (int i = 0; i < size; i++) {
                list.addAll(list.removeFirst().list);
            }
        }
        return height;
    }
}


2018-09-07 10:21
1
# -*- coding=utf-8 -*-
import sys
from collections import defaultdict


def get_max_depth(temp_dict):
    """得到最大深度:此时计算的深度为边的个数,

    
    Args:
        temp_dict (dict): 保存有节点单向连边的字典
    
    Returns:
        int: 最大深度
    """

    res = []
    length = 0
    for item in temp_dict[1]:
        get_depth(item, temp_dict, length, res)

    return max(res)


def get_depth(item, temp_dict, length, res):
    """取得从节点1每一个子树出发的最大深度
    
    Args:
        item (int): 当前节点
        temp_dict (dict): 保存有连接情况的节点
        length (int): 当前长度
        res (list): 保存有最大深度的数组
    """

    length += 1
    if not temp_dict[item]:
        res.append(length)
        return
    for item in temp_dict[item]:
        get_depth(item, temp_dict, length, res)


if __name__ == "__main__":
    n = int(sys.stdin.readline().strip())

    temp_dict = defaultdict(list)

    for i in range(n-1):
        n1, n2 = map(int, sys.stdin.readline().strip().split())
        if n2 < n1:
            n1, n2 = n2, n1
        temp_dict[n1].append(n2)

    print(2 * (n - 1) - get_max_depth(temp_dict))


2018-09-07 10:34
2
coder_JB4V922C 回复 coder_8T7A8B5A

if n2 < n1: n1, n2 = n2, n1

如果 不是小数在树的上层,而是乱序的,这里该怎么办呢?

2018-09-07 15:31

相当于一棵树,找到到子节点最长的一条路径,不用走俩次,其他的都需要走俩次。

这道题先用python写的,只通过73%,换成C++就AC了。

#include <iostream>
#include <vector>

using namespace std;

vector<vector<int>> v;

int fd(int father,int current)
{
    int i;
    int max_len=0,len;
    for(i=0;i<v[current].size();i++)
    {
        if(v[current][i]!= father)
        {
            len=fd(current,v[current][i]);
            if(len>max_len)max_len=len;
        }
    }
    return max_len+1;
}

int main() {

    int n;
    cin >> n;
    int i;
    int x, y;
    v.resize(n);
    for (i = 0; i < n - 1; i++)
    {
        cin >> x >> y;
        v[x - 1].push_back(y - 1);
        v[y - 1].push_back(x - 1);
    }
    int max_len = 0, len;
    for (i = 0; i < v[0].size(); i++)
    {
        len = fd(0, v[0][i]);
        if (len > max_len)max_len = len;
    }
    cout<<max_len+(n-max_len-1)*2<<endl;
    return 0;
}


2018-09-07 16:53
coder_8T7A8B5A 回复 coder_JB4V922C

你说的对,我这样做的确是有问题的。

2018-09-07 18:07

第一题图的遍历,思路 (n-1)*2-图最大遍历深度

AC代码:

#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
#define MAXN 100010
int g[MAXN][100];
int f[MAXN];
int isv[MAXN];
int maxn = 0;

void dfs(int curp,int curs)
{
    int i;
    // 当前节点走过了
    isv[curp] = 1;
    // 当前节点是否全部走过,默认为是
    bool isallv = true;
    for(i=0;i<f[curp];i++){
        if( ! isv[g[curp][i]]){
            // 当前节点的第 i 个子节点没有被走过,那么可以走
            isallv = false;
            dfs(g[curp][i],curs+1);
        }
    }
    if(isallv){
        //当前节点的所有子节点都被走过,那么不能再走了,计算走到这儿的最大深度
        if(curs>maxn)
            maxn = curs;
    }
}

int main()
{
    int n;
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        memset(g,0,sizeof(g));
        memset(f,0,sizeof(f));
        memset(isv,0,sizeof(isv));
        for(int i=0;i<n-1;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            g[a][f[a]++] = b;
            g[b][f[b]++] = a;
        }
        dfs(1,0);
        printf("%d\n",(n-1)*2-maxn);
    }
    return 0;
}


2018-09-07 18:58

!!!!!我来说说的的思路!!!!!!


总步数量(sumsteps)


  1. 递归过程:去掉网络所有度为1的节点(sumsteps += 去掉的边数 * 2),直到所有节点去掉(或剩下1个节点)

  2. 深度搜索:搜索一条从起点1 到 重点N的路(由于生成树,路径唯一),sumsteps -= 路径长度


得到最短的路径,sumsteps!



我还没编程实现,画了几个topo简单测试了行,可行,大家看思路行吗!!!


2018-09-08 17:18
coder_JyN35QAd 回复 coder_JyN35QAd

好像根本不用递归,找到路径就行了,其他的边*2,见笑见笑

2018-09-08 17:29
添加回复
回到顶部