C++之求有向无环图的最长路径(拓扑排序+动态规划)

一、创建有向无环图:

      我用的数据存储结构是邻接矩阵,如果用邻接表也是可以的。这里就不多作解释,直接进入关键代码部分。

        

      二、拓扑排序:

      官方解释是:对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。

      用通俗的话来讲就是,比如你要选课A、B、C,但是读B要先选A,读A要先选C,所以,进行拓扑排序后,就是C、A、B。

      所以在创建图的时候,要用一个标志数组indegree[]来保存每个顶点的入度,另外,在进行下面求最长路径的时候,需要用到拓扑排序的结果,所以还需要一个数组topo[]来记录拓扑排序的结果。

      拓扑排序里,都先找到入度为0的顶点,然后把它取出来(让它的入度等于负数进行实现),保存在topo[]里面,接着更新其他与这个顶点相邻的其他顶点的入度,最后,循环即可。

     

     三、求最长路径:

     只要知道了动态规划的公式就好,这里直接给出公式maxPath[v]=max{maxPaht[v],maxPath[k]+e[k][v]},其中maxPath[v]表示的是起始点到点v的最长路径,e[k][v]表示点k到点v的距离。这个公式的意思是,假如有A、B、C三个点,求maxPath[C],要么直接取maxPath[C],要么取maxPath[A]+e[A][C],要么取maxPath[B]+e[B][C]。当然,这个公式的所需要的顶点需要按顺序取自拓扑排序的结果,否则,有可能在进行求maxPath[k]+e[k][v]的时候,maxPath[k]并不是最长的,造成结果出错。

   

       四、求最长路线:

     这个我想了挺久的,最后想到的方法是用一个二维矩阵(N*N)保存每一个点到点的最长路径,对第N列进行排序,取出第N列中最大的一个数a,记录下行号R和列号C(在v1—>v2中,行对应的是v1,列对应的是v2),然后把列号C压进栈里(输出的时候直接输出就是最长路线了),接着按照这个数字a的行号R找到相同数字的列号R'(比如a数字在的位置是[4][5],行号是4,则找到第4列),令N=R‘,重复以上步骤即可。



#include <iostream>  
#include <algorithm>  
#include <stack>  
using namespace std;  
  
char *v;//顶点数组,下标为顶点号,值为顶点名称(用在创建有向无环图中)  
int **e;//边的二维矩阵(用在创建有向无环图中)  
  
int *indegree;//保存顶点入度数的数组(求拓扑排序)  
int *topo;//保存拓扑排序结果的数组(求拓扑排序)  
  
int *maxPath;//保存到此点的最长路径(求最长路径)  
  
//创建有向无环图  
void creatGraph(int vSize,int eSize)  
{  
    //初始化  
    int i,j,k,c;  
    char a,b;  
    indegree=new int[vSize];  
    topo=new int[vSize];      
    v=new char[vSize];  
    e=new int*[vSize];  
    for(i=0;i<vSize;i++)  
         e[i]=new int[vSize];  
    for(i=0;i<vSize;i++)  
        for(j=0;j<vSize;j++)  
            e[i][j]=0;  
    for(i=0;i<vSize;i++)  
    {  
        indegree[i]=0;  
        topo[i]=0;  
    }  
  
    //建图  
    cout<<endl<<"请输入各顶点名称:";  
    for(i=0;i<vSize;i++)  
        cin>>v[i];  
    cout<<endl<<"请先后输入顶点V1和V2(表示V1->V2)以及权值,按换行键继续"<<endl;  
    for(i=0;i<eSize;i++)  
    {  
        cin>>a>>b>>c;  
        for(j=0;j<vSize;j++)   
           if(v[j]==a) break;  
        for(k=0;k<vSize;k++)  
           if(v[k]==b) break;  
           e[j][k]=c;  
           indegree[k]++;//入度+1  
  
    }  
}  
  
//拓扑排序  
void topologicalSort(int vSize)  
{  
    int i,j,k;  
    for(i=0;i<vSize;i++) //vSize次循环  
    {  
        j=0;  
        while(j<vSize&&indegree[j]!=0) j++;//找到入度为0的顶点  
        topo[i]=j;//保存  
        indegree[j]=-1;// 设结点j为入度为-1,以免再次输出j  
          
        for(k=0;k<vSize;k++)//更新其他入度点  
            if(e[j][k]!=0)  
                indegree[k]--;  
    }  
}  
  
//最长路径  
void getMaxPath(int vSize)  
{  
    //初始化  
    int i,j,k,v1,v2,max=0,**path,*p;  
    maxPath=new int[vSize];            //保存最长路径,表示到顶点N的最长路径  
    p=new int[vSize];                  //用来处理最长路线的选择顶点  
    path=new int*[vSize];              //用来保存点到点的最长路径矩阵  
    for(i=0;i<vSize;i++)  
         path[i]=new int[vSize];  
    for(i=0;i<vSize;i++)  
        for(j=0;j<vSize;j++)  
            path[i][j]=0;  
    for(i=0;i<vSize;i++)  
    {     
        maxPath[i]=0;  
        p[i]=0;       
    }  
  
        //关键代码,求最长路径  
        for(j=0;j<vSize;j++)  
        {  
            v2=topo[j];  
            for(k=0;k<j;k++)  
            {  
                 v1=topo[k];  
                 if(e[v1][v2]!=0)                     //表示有路  
                 {     
                     if(maxPath[v1]+e[v1][v2]>maxPath[v2])  
                        maxPath[v2]=maxPath[v1]+e[v1][v2];  
  
                     if(maxPath[v2]>max)  
                     {  
                        max=maxPath[v2];//保存长度  
                        path[v1][v2]=max;                                          
                     }  
                 }  
            }                     
        }  
  
        //求最长路线  
        stack<int> s;//保存线路  
        for(i=vSize-1;i>0;)  
        {  
            for(j=0;j<vSize;j++)  
            {  
                p[j]=path[j][i];                                  
            }  
            sort(p,p+vSize);  
            int maxValue=p[j-1];  
            if(maxValue!=0)    
            {  
                for(j=0;j<vSize;j++)  
                {  
                    if(path[j][i]==maxValue)  
                    {  
                       s.push(i);  
                       i=j;  
                       if(i==0)  
                          s.push(i);  
                       break;  
                    }    
                }  
            }  
        }  
  
        //输出结果  
        cout<<endl<<"最长路径的长度是:"<<max<<endl<<"最长路径为:";  
        int len=s.size();  
        for(i=0;i<len;i++)  
        {  
            if(i!=0) cout<<" -> ";   
            cout<<v[s.top()];  
            s.pop();  
        }         
        cout<<endl<<endl;  
}  
  
int main()  
{  
    int vSize,eSize,i;  
    while(1)  
    {  
        cout<<"·请分别输入有向无环图的顶点数和边数:";  
        cin>>vSize>>eSize;  
  
        creatGraph(vSize,eSize);//创建图  
        topologicalSort(vSize);//拓扑排序  
        getMaxPath(vSize);//最长路径  
              
    }  
    return 0;  
}  


  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值