题目

给定一个表示番薯洞的地图,每个番薯洞中有一定数量的番薯。
已知番薯洞的连接路径,并规定路径是单向且小序号指向大序号,也无环。
可以从任意一处开始挖,然后沿着连接往下挖(仅能选择一条路径),当无连接时,结束。
设计一种挖番薯的方案,使得可以挖到更多的番薯。
输出路径。

方法一 深度优先搜索(超时)

使用 记录每个节点可以指向的节点。
从 1 到 n 遍历每个节点,使用 DFS 从该节点开始访问连接指向的所有细胞。在搜索期间更新番薯的最大数量,并更新路径。
在搜索完成后返回答案。
记录节点是否有前驱节点。

C++代码

class Solution {
    vector<vector<int>> next;
    vector<bool> vis;
    int maxCnt;
    string maxPath;

    void dfs(int a, int cnt, string path, vector<int>& potatoNum){
        for(int i=0; i<next[a].size(); ++i){
            int b = next[a][i];
            vis[b] = true;
            int itmp = cnt + potatoNum[b-1];
            string stmp = path + "-" + to_string(b);
            if(itmp > maxCnt){
                maxCnt = itmp;
                maxPath = stmp;
            }
            dfs(b, itmp, stmp, potatoNum);
        }
    }
public:
    /**
     *
     * @param potatoNum int整型vector 依次表示序号为1,2,3..的番薯洞各有多少个番薯
     * @param connectRoad int整型vector<vector<>> 每个一维数组[x,y]表示从第x号番薯洞到第y号有路
     * @return string字符串
     */
    string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
        // write code here
        int n = potatoNum.size();
        next.resize(n+1);
        for(auto &r : connectRoad){
            next[r[0]].push_back(r[1]);
        }
        maxCnt = 0;
        vis.resize(n+1, false);
        for(int i=1; i<=n; ++i){
            if(vis[i])
                continue;
            int cnt = potatoNum[i-1];
            string path = to_string(i);
            if(cnt > maxCnt){
                maxCnt = cnt;
                maxPath = path;
            }
            dfs(i, cnt, path, potatoNum);
        }

        return maxPath;
    }
};

方法二 动态规划

方法一有很多重复的计算。
例如:

[5,10,20,5,4,5],[[1,2],[1,4],[2,4],[3,4],[4,5],[4,6],[5,6]]

在节点 1 中计算了 1-2-4-5-6,在节点 3 中计算了 3-4-5-6,重复计算了路径 4-5-6。

为了防止这些重复计算,换一下思路,从 n 到 1 遍历节点。

表示从节点 开始到无连接时的最大番薯数目:取节点 的后继节点的最大值,再加上节点 本身的值。
状态转移方程: 记录每个节点的后继节点。
最后,遍历所有节点,返回其最大值对应的路径。

C++代码

class Solution {
public:
    /**
     * 
     * @param potatoNum int整型vector 依次表示序号为1,2,3..的番薯洞各有多少个番薯
     * @param connectRoad int整型vector<vector<>> 每个一维数组[x,y]表示从第x号番薯洞到第y号有路
     * @return string字符串
     */
    string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
        // write code here
        int n = potatoNum.size();
        vector<vector<int>> next(n+1);
        for(auto &r : connectRoad){
            next[r[0]].push_back(r[1]);
        }

        vector<int> cnt(n+1, 0);
        vector<string> path(n+1, "");
        for(int i=n; i>=1; --i){
            cnt[i] = potatoNum[i-1];
            path[i] = to_string(i);

            int tmp = 0;
            int e = 0;
            for(int j=0; j<next[i].size(); ++j){
                int b = next[i][j];
                if(cnt[b] > tmp){
                    tmp = cnt[b];
                    e = b;
                }
            }
            if(tmp){
                cnt[i] += tmp;
                path[i] += "-" + path[e];
            }
        }

        int maxCnt = 0;
        string maxPath = "";
        for(int i=1; i<=n; ++i){
            if(cnt[i] > maxCnt){
                maxCnt = cnt[i];
                maxPath = path[i];
            }
        }
        return maxPath;
    }
};

方法三 记忆化深度优先搜索

记忆化深度优先搜索与朴素深度优先搜索的区别:前者使用缓存存储了节点的数据,这样可以不用多次重复计算同一个节点。

此处的 与方法二中的意思不同,表示以节点 为结尾的路径中的最大番薯数目。

函数 中有一个判断条件 ,这表示这条路径还有进行延伸的价值,更新

C++代码

class Solution {
    vector<vector<int>> next;
    vector<int> cnt;
    vector<string> path;

    void dfs(int a, vector<int>& potatoNum){
        for(int i=0; i<next[a].size(); ++i){
            int b = next[a][i];
            if(cnt[b] < cnt[a] + potatoNum[b-1]){
                cnt[b] = cnt[a] + potatoNum[b-1];
                path[b] = path[a] + "-" + to_string(b);
                dfs(b, potatoNum);
            }
        }
    }
public:
    /**
     * 
     * @param potatoNum int整型vector 依次表示序号为1,2,3..的番薯洞各有多少个番薯
     * @param connectRoad int整型vector<vector<>> 每个一维数组[x,y]表示从第x号番薯洞到第y号有路
     * @return string字符串
     */
    string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
        // write code here
        int n = potatoNum.size();
        next.resize(n+1);
        for(auto &r : connectRoad){
            next[r[0]].push_back(r[1]);
        }
        cnt.resize(n+1, 0);
        path.resize(n+1, "");

        for(int i=1; i<=n; ++i){
            if(cnt[i] < potatoNum[i-1]){
                cnt[i] = potatoNum[i-1];
                path[i] = to_string(i);
                dfs(i, potatoNum);
            }
        }

        int maxCnt = 0;
        string maxPath = "";
        for(int i=1; i<=n; ++i){
            if(cnt[i] > maxCnt){
                maxCnt = cnt[i];
                maxPath = path[i];
            }
        }
        return maxPath;
    }
};