题意:

给你一张有向无环图,图中每个点都有一个权值,找出一条路径使得路径经过的点权值和最大,输出这条路径。

解法一(记忆化搜索):

我们记 图片说明 表示以点 图片说明 为起点能够获得的最大权值和,图片说明 表示以点 图片说明 为起点能够获得最大权值和的前提下下一个点的编号。
对于 图片说明 ,显然有 图片说明

对于 图片说明 ,显然就是使得 图片说明 最大的那个 图片说明
例如本题样例(为了简化,我们图中只表示了f[i][0]):

具体的,我们可以开一个标记数组 图片说明 记录某个节点是否计算过,然后再枚举每个节点为起点进行递归求解。
代码:
class Solution {
public:
    /*以下是动态分配内存*/
    int** f;
    //f[i][0] 表示以点i为起点开始走能够获得的最大权值和
    //f[i][1] 表示以点i为起点开始走要获得最大权值和的下一个点的编号
    int* val;//val[i] 表示点i的权值
    bool* calc;// calc[i] 记录点i是否计算过
    vector<int>* g;//存图
    void dfs(int u){
        if(calc[u])return;//记忆化搜索
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i];
            dfs(v);
            if(f[u][1]==0||f[v][0]>f[f[u][1]][0]){//选一个权值和最大的后继节点
                f[u][1]=v;
            }
        }
        f[u][0]=f[f[u][1]][0]+val[u];//加上当前节点的权值
        calc[u]=true;//标记已经计算过了
    }
    string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
        int n=potatoNum.size();
        val=new int[n+1]{0};//动态分配内存
        for(int i=0;i<potatoNum.size();i++){
            val[i+1]=potatoNum[i];
        }
        f=new int*[n+1];
        for(int i=0;i<=n;i++){
            f[i]=new int[2]{0};
        }
        calc=new bool[n+1]{false};
        g=new vector<int>[n+1];
        for(int i=0;i<connectRoad.size();i++){
            int u=connectRoad[i][0],v=connectRoad[i][1];
            if(u>v)swap(u,v);
            g[u].push_back(v);//小的指向大的
        }
        for(int i=1;i<=n;i++){
            dfs(i);//每个点为起点遍历
        }
        int t=0;
        for(int i=1;i<=n;i++){
            if(t==0||f[i][0]>f[t][0])t=i;//寻找起点
        }
        string ans;
        stringstream ans_stream;
        while(t){
            ans_stream<<t;
            if(f[t][1]){
                ans_stream<<"-";//记录路径
            }
            t=f[t][1];
        }
        ans_stream>>ans;
        return ans;
    }
};

时间复杂度:图片说明图片说明 表示点的数量,图片说明 表示边的数量。由于每个点只会计算一次,程序中求解的过程相当于只遍历了一次整张图,故总的时间复杂度为图片说明
空间复杂度:图片说明 ,使用邻接表存图占图片说明 级别的内存,图片说明数组都占图片说明 级别的内存,故总的空间复杂度为图片说明

解法二(拓扑排序):

由于这是一张有向无环图,我们可以按照倒着的拓扑序从后往前递推上述 图片说明 数组。
具体的,我们首先对整张图进行拓扑排序,然后再按照倒着的拓扑序进行递推求解,递推的方法根上述一样。
代码:

class Solution {
public:
    int** f;
    //f[i][0] 表示以点i为起点开始走能够获得的最大权值和
    //f[i][1] 表示以点i为起点开始走要获得最大权值和的下一个点的编号
    int* val;//val[i] 表示点i的权值
    bool* calc;// calc[i] 记录点i是否计算过
    vector<int>* g;//存图
    int* incnt;//incnt[i] 表示点i的入度
    int* ord;//ord数组存放拓扑序列
    string digSum(vector<int>& potatoNum, vector<vector<int> >& connectRoad) {
        int n=potatoNum.size();
        //以下是动态分配内存
        f=new int*[n+1];
        for(int i=0;i<=n;i++){
            f[i]=new int[2]{0};
        }
        val=new int[n+1]{0};
        for(int i=0;i<n;i++){
            val[i+1]=potatoNum[i];
        }
        calc=new bool[n+1]{false};
        g=new vector<int>[n+1];
        incnt=new int[n+1]{0};
        ord=new int[n+1]{0};
        for(int i=0;i<connectRoad.size();i++){
            int u=connectRoad[i][0],v=connectRoad[i][1];
            //建图
            if(u>v)swap(u,v);
            incnt[v]++;
            g[u].push_back(v);
        }
        queue<int> que;
        for(int i=1;i<=n;i++){
            if(!incnt[i])que.push(i);//入度为0的点进队列
        }
        int k=0;
        while(!que.empty()){//拓扑排序
            int u=que.front();
            que.pop();
            ord[++k]=u;
            for(int i=0;i<g[u].size();i++){
                int v=g[u][i];
                if(!--incnt[v]){
                    que.push(v);
                }
            }
        }
        //反转后刚好是倒着的拓扑序,需要从后往前递推
        reverse(ord+1,ord+1+k);
        for(int i=1;i<=k;i++){//开始递推
            int u=ord[i];
            for(int j=0;j<g[u].size();j++){
                if(f[u][1]==0||f[g[u][j]][0]>f[f[u][1]][0]){
                    f[u][1]=g[u][j];
                }
            }
            f[u][0]=f[f[u][1]][0]+val[u];
        }
        int t=1;
        for(int i=2;i<=n;i++){
            if(f[i][0]>f[t][0])t=i;//找起点
        }
        stringstream ans_stream;
        string ans;
        while(t){
            ans_stream<<t;
            if(f[t][1])ans_stream<<"-";
            t=f[t][1];
        }
        ans_stream>>ans;
        return ans;
    }
};

时间复杂度:图片说明 ,拓扑排序和递推求解的过程相当于分别遍历了一遍整张图,故总的时间复杂度为图片说明
空间复杂度:图片说明 ,使用邻接表存图占图片说明 级别的内存,图片说明数组都占图片说明 级别的内存,故总的空间复杂度为图片说明