zoukankan      html  css  js  c++  java
  • codility上的问题(34) Fluorum 2014

    好久没写codility的题了。一来没时间,二来有的题目不太好分析。这个题比較有意思,我还没有给出很严格的证明。


    给定一棵树(无向无环图)。从一个节点出发,每次选择一个节点,从起点到目的节点的路径上没经过的节点尽可能多,直到遍历全然部的节点。假设起点到两个目的节点的路径中没经过的节点相同多,则选择标号较小的节点作为目的节点。如此继续,直到遍历全部的节点,求按顺序选择了哪些目的节点?

    比如从2 開始。第一次选择0。然后1,0变为经历过的节点。

    然后从0開始,第二次选择6。 然后4,6变为经历过的节点。

    然后从6開始,第三次选择3。然后3变为经历过的节点。

    然后从3開始,最后一次选择5。然后5变为经历过的节点。

    输出[2,0,6,3,5]

    函数头部是 vector<int> solution(int K, vector<int> &T);

    当中K是起点编号。(i,T[i])表示树的一条边。

    变量范围节点数N, [1..90,000],T元素范围[0..N-1]

    要求时间复杂度O(N),空间复杂度O(N)。

    分析: 我认为这个题我没能完美的解决,由于没给出严格的证明。首先目标节点肯定是叶子,直观上深度更深的叶子更有效。

    所以我按叶子深度由大到小对叶子排序,假设深度相同。我把编号小的叶子放前面。依照这个既定顺序给叶子定义权值,这个权值就是最后訪问路径上经历的新节点数(这个须要严格证明)。权值的定义例如以下。沿着叶子不断向父亲走。直到第一个标记过的节点停止。这条路径上的节点数作为叶子的权值。而且这条路径上的节点都设置未标记过。关键问题是,要依照之前排好的顺序给每一个叶子算权值(由于顺序会影响叶子的权值)。

    直观感受是,假设两个叶子在一个分叉上。显然深的节点更先被訪问,假设不在一个分叉上。那么先算深的也没什么损失。最后。依照这个权值再对叶子排序一次,就是所要的结果。为了满足时间复杂度。我採用的是基数排序,写了一个help函数完毕排序。

    所以大概思路就是:

    (1) 先dfs一次,得到每一个叶子的深度和每一个节点的父亲

    (2) 对全部叶子按深度进行基数排序

    (3) 依照排好的顺序计算每一个叶子的权值(复杂度相当于遍历树,由于是沿着叶子向上遍历的)

    (4) 依照计算的权值,再对叶子做一次基数排序 得到终于结果。

    终于代码:

    // you can also use includes, for example:
    // #include <algorithm>
    
    void help(int n, vector<pair<int,int> > &v) {  // (id, weight) 
        vector<vector<int> > have;
        have.resize(n);
        for (int i = 0; i < v.size(); ++i) {
            have[v[i].first].push_back(v[i].second);
        }
        v.clear();
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < have[i].size(); ++j) {
                v.push_back(make_pair(i, have[i][j]));
            }
        }
        have.clear();
        have.resize(n);
        for (int i = 0; i < v.size(); ++i) {
            have[v[i].second].push_back(v[i].first);
        }
        v.clear();
        for (int i = n - 1; i >= 0; --i) {
            for (int j = 0; j < have[i].size(); ++j) {
                v.push_back(make_pair(have[i][j] , i));
            }
                
        }
    }
    void dfs(int x,int p,int d,vector<int> &depth, vector<int> &parent,vector<vector<int> > & con) {
        parent[x] = p;
        depth[x] = d;
        for (int i = 0; i < con[x].size(); ++i) {
            if (con[x][i] != p) {
                dfs(con[x][i], x, d + 1, depth, parent, con);
            }
            
        }
        
    }
    vector<int> solution(int K, vector<int> &T) {
        // write your code in C++98
        vector<vector<int> > con;
        int n = T.size();
        con.resize(n);
        for (int i = 0; i < n; ++i) {
            if (T[i] != i) {
                con[i].push_back(T[i]);
                con[T[i]].push_back(i);
            }
        }
        vector<int> parent, depth;
        depth.resize(n);
        parent.resize(n);
        dfs(K, -1, 0, depth, parent, con);
        vector<pair<int,int> > v;
        for (int i = 0; i < n; ++i) {
            if ((i != K)  && (con[i].size() == 1)) {  //leaf
                v.push_back(make_pair(i, depth[i]));
            }
        }
        help(n,v);
        vector<bool> mark;
        mark.resize(n, false);
        for (int i = 0; i < v.size(); ++i) {
            int x = -1;
            for (int j = v[i].first; (j >= 0) && (!mark[j]); ++x, j = parent[j]) {
                mark[j] = true;
            }
            v[i].second = x;
        }
        help(n,v); 
        vector<int> result;
        result.push_back(K);
        for (int i = 0; i < v.size(); ++i) {
            result.push_back(v[i].first);
        }
        return result;
    }
    
    
    






  • 相关阅读:
    TVM性能评估分析(七)
    TVM性能评估分析(六)
    理论三:里式替换(LSP)跟多态有何区别?哪些代码违背了LSP?
    理论二:如何做到“对扩展开放、修改关闭”?扩展和修改各指什么?
    (转)浅谈 SOLID 原则的具体使用
    老一辈的革命先烈告诉我,凡事打好提前量
    理论一:对于单一职责原则,如何判定某个类的职责是否够“单一”?
    实战二(下):如何利用面向对象设计和编程开发接口鉴权功能?
    实战二(上):如何对接口鉴权这样一个功能开发做面向对象分析?
    12 | 实战一(下):如何利用基于充血模型的DDD开发一个虚拟钱包系统?
  • 原文地址:https://www.cnblogs.com/jhcelue/p/7283175.html
Copyright © 2011-2022 走看看