题意:
给你一张有向无环图,图中每个点都有一个权值,找出一条路径使得路径经过的点权值和最大,输出这条路径。
解法一(记忆化搜索):
我们记 表示以点
为起点能够获得的最大权值和,
表示以点
为起点能够获得最大权值和的前提下下一个点的编号。
对于 ,显然有
对于
,显然就是使得
最大的那个
例如本题样例(为了简化,我们图中只表示了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;
}
}; 时间复杂度: ,拓扑排序和递推求解的过程相当于分别遍历了一遍整张图,故总的时间复杂度为
空间复杂度: ,使用邻接表存图占
级别的内存,
数组都占
级别的内存,故总的空间复杂度为

京公网安备 11010502036488号