7-5 最长路径 AC代码

最长路径问题

题目

给定一个正权有向无环图和图中的两个顶点,请编写程序找出这两个顶点间的最长路径,若两点间存在多条最长路径,则输出字典序最小者。假定图中包含n个顶点,编号为0至n-1。

注:字典序,即对象在字典中的顺序。对于两个数字序列,从第一个数字开始比较,当某一个位置的数字不同时,该位置数字较小的序列,字典序较小,例如1 2 3 9比1 2 4 5小,1 2 8 9比1 2 10 3小。

  • 输入格式:

输入第一行为3个正整数n和e,分别为图的顶点数和边数,均不超过50。接下来e行表示每条边的信息,每行为3个整数a、b、c,其中a和b表示该边的端点编号,c表示权值。各边并非按端点编号顺序排列。接下来1行为两个整数s和t,表示两个顶点编号。

  • 输出格式:

输出顶点s到顶点t的字典序最小最长路径,路径中顶点编号用“->”间隔。如s和t不连通,则输出“none”。

输入样例:

4 4
0 1 1
1 2 1
0 3 1
3 2 2
0 2

输出样例:

0->3->2

代码长度限制
16 KB
时间限制
100 ms
内存限制
64 MB

思路

这题的正规解法:是有向无环图,先拓扑排序,再用动态规划求解。
不能使用dijkstra算法,因为dijk算法不能修改已经探索过一次的节点,但是我们可能会找到一条更长的路径来到达某个结点,这就会产生矛盾。例如这次加入结点u,最长路为10,下次有可能加入一个结点v,使得 u通过v到源点的距离大于10,但由于u在之前已经被加入到集合中,无法再更新,导致结果是不正确的。

如果取反用dijkstra求最短路径呢,记住,dijkstra不能计算有负边的情况。。。

但是这题又需要按照字典序来得到最恰当的那条路径,很烦人。因为我的深度优先算法dfs运用的不是很理想,不太会回溯,所以就另辟蹊径了。
首先看到这题最多只有50个结点,那有向边的数量就超不过2500条,对于这个小量级的数据,我可以在一开始就将它们按照顺序排好,起始节点小的优先,终点节点小的次优先,因我我使用的建图方法是链式头插法建图,对于同一个节点的所有边,遍历顺序是和插入顺序相反的。所以我只要在拓扑排序的过程中,贪心判断规则改为>=,就可以保证每个点保存的last节点都是最小字典序的。
这个方法应该是存在漏洞的,但是好歹也是AC了,哈哈哈
在这里插入图片描述

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define maxn 55
#define maxm 2505
using namespace std;
int tot,n,m,s,e,flag;
int len[maxm];
int v[maxm];
int head[maxn];
int vis[maxn];
int nxt[maxm];
int maxlen[maxn];
int in[maxn];
int out[maxn];
int last[maxn];
int depth[maxn];
int deep;
queue<int> q;
struct EDGE{
   int u;
   int v;
   int len;
}edge[maxm];
bool comp(const struct EDGE e1,const struct EDGE e2){
   if(e1.u<e2.u){
       return true;
   }else if(e1.u==e2.u){
       if(e1.v<e2.v){
           return true;
       }
   }
   return false;
}

void init(){
   for(int i=0;i<n;i++){
       last[i]=-1;
   }
}

void ins(int start,int end,int l){
   ++tot;
   len[tot]=l;
   v[tot]=end;
   nxt[tot]=head[start];
   head[start]=tot;
}

void topo(){
   while(!q.empty()){
       int t = q.front();
       q.pop();
       vis[t]=1;

       for(int i=head[t];i!=0;i=nxt[i]){
           int j=v[i];

           if(j==e){
               flag=1;
           }
           if(!vis[j]){
               if(maxlen[t]+len[i]>=maxlen[j]){
                   maxlen[j]=maxlen[t]+len[i];
                   last[j]=t;
                   depth[j]=depth[t]+1;
               }
               in[j]--;
               if(in[j]==0){
                   q.push(j);
               }
           }
       }
   }
}

void print(){
   for(int i=0;i<n;i++){
       cout<<i<<" maxlen: "<<maxlen[i]<<" last:"<<last[i]<<" depth:"<<depth[i]<<endl;
   }
}

void getList(int node){
   if(node==-1) return;
   getList(last[node]);
   if(node==e)
       cout<<node;
   else
       cout<<node<<"->";
}

int main(){
   cin>>n>>m;
   init();
   int start,end,l,ecnt=0;
   for(int i=0;i<m;i++){
       cin>>start>>end>>l;
       edge[++ecnt].u=start;
       edge[ecnt].v=end;
       edge[ecnt].len=l;
   }
   sort(edge+1,edge+1+ecnt,comp);
   for(int i=1;i<=ecnt;i++){
       ins(edge[i].u,edge[i].v,edge[i].len);
       in[edge[i].v]++;
       out[edge[i].u]++;
   }
   cin>>s>>e;
   q.push(s);
   depth[s]=1;
   res[++deep] = s;
   topo();
   //print();
   if(!flag){
       cout<<"none";
   }else{
       getList(e);
      // cout<<e;
   }
   
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值