SDUTOJ 2498 AOE网上的关键路径 最短路spfa

Description

    一个无环的有向图称为无环图(Directed Acyclic Graph),简称DAG图。
    AOE(Activity On Edge)网:顾名思义,用边表示活动的网,当然它也是DAG。与AOV不同,活动都表示在了边上,如下图所示:
                                     
    如上所示,共有11项活动(11条边),9个事件(9个顶点)。整个工程只有一个开始点和一个完成点。即只有一个入度为零的点(源点)和只有一个出度为零的点(汇点)。
    关键路径:是从开始点到完成点的最长路径的长度。路径的长度是边上活动耗费的时间。如上图所示,到 579是关键路径(关键路径不止一条,请输出字典序最小的),权值的和为18

Input

    这里有多组数据,保证不超过10组,保证只有一个源点和汇点。输入一个顶点数n(2<=n<=10000),边数m(1<=m <=50000),接下来m行,输入起点sv,终点ev,权值w1<=sv,ev<=n,sv != ev,1<=w <=20)。数据保证图连通。

Output

    关键路径的权值和,并且从源点输出关键路径上的路径(如果有多条,请输出字典序最小的)。

Sample Input

9 11
1 2 6
1 3 4
1 4 5
2 5 1
3 5 1
4 6 2
5 7 9
5 8 7
6 8 4
8 9 4
7 9 2

Sample Output

18
1 2
2 5
5 7
7 9

Hint

 
其实就是求最长路加了一个限制条件

开始用的是disktra改了改模版发现不对,数组开不开也就改不动了
#include <stdio.h>
#define maxnum 1000
#define maxint 999999

int dist[maxnum]; //表示当前点到源点的最短路径长度
int prev[maxnum];   //记录当前点的前一个结点
int c[maxnum][maxnum];  //记录图的两点间路径长度
int n,line;            //图的结点数和路径数


void Dij(int n,int v,int *dist, int *prev, int c[maxnum][maxnum]) //v代表源点吗?
{
    int s[maxnum] = {0};//判断是否已将该点存入到s数组中
    int i;
    for(i = n;i>=1;i--)
    {
        dist[i] = c[v][i];//将从源点到i点的初始距离存入数组dist中
        s[i] = 0;//初始化为全部没有用过该点
        if(dist[i] == 0)//如果源点到该点的距离为无穷大,则该点前面没有连接点
            prev[i] = 0;
        else
            prev[i] = v;
    }
    dist[v] = 0;    //源点到源点的距离初始化为0
    s[v] = 1;//把源点存入数组s

    //依次将未放入集合s中的节点,取dist【】最小值的节点放进去
    //一旦s包含了所有v中顶点,dist就记录了从源点到所有其他顶点之间的最短路径
    //注意是从第二个节点开始,第一个结点表示源点
   for(i = 2;i<=n;i++)
   {
       int tmp = 0;
       int u = v;
       for(int j = n;j>=1;j--)  //找出当前未使用的点j的dist【j】最小值
       {
           if((!s[j]) && dist[j] > tmp)
           {
               u = j;               //u储存当前邻接点中距离最小的点的号码
               tmp = dist[j];
           }
       }
       s[u] = 1;        //一次for循环结束后,便找出了所有未使用的点中距离最小的点,将此点存入集合s中

       //更新dist值

      for(int j = n;j>=1;j--)
       {
           if((!s[j]) && c[u][j]>0) //确定该点没有加入s集合,并且存在这段距离,即经过该点
           {
               int newdist = dist[u] + c[u][j];//判断经过该点到j的距离和不经过该点到j的距离的大小
               if(newdist > dist[j])
               {
                   dist[j] = newdist;
                   prev[j] = u;
               }
           }
       }
   }
}

//查找从源点v到终点u的路径,并输出

void searc(int *prev,int v,int u)
{
    int que[maxnum];        //该数组保存路径
    int tot = 1;
    que[tot] = u;       //从终点开始找,一直找到源点v并记录路径
    tot++;
    int tmp = prev[u];
    while(tmp != v)
    {
        que[tot] = tmp;
        tot++;
        tmp = prev[tmp];
    }
    que[tot] = v;
    for(int i = tot;i>=1;i--)//输出
    {
        if(i!=1)
            printf("%d->",que[i]);
        else
            printf("%d\n",que[i]);
    }
    //注意,若题目要求输出距离,则直接输出dist【u】即可
}

int main()
{
    scanf("%d %d",&n,&line);

    int p,q,len;
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
            c[i][j] = 0;
    }
    for(int i = 1;i<=line;i++)
    {
        dist[i] = 0;
    }
    for(int i = 1;i<=line;i++)
    {
        scanf("%d%d%d",&p,&q,&len);
        if(len>c[p][q])
        {
            c[p][q] = len;
            //c[q][p] = len;
        }
    }
    Dij(n,1,dist,prev,c);
    printf("从源点到终点的最短路径长度为%d\n",dist[n]);
    searc(prev,1,n);
    return 0;
}
下面是spfa的方法
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#define INF 0x3f3f3f3f
using namespace std;
struct node
{
    int u,v,w,next;
} edge[500010];
int cnt,n,m,head[10010],dis[10010],vis[10010],pre[10010], pre1[10005];
int id[10005],cd[10005];
void add(int u,int v,int len)
{
    edge[cnt].u=u;
    edge[cnt].v=v;
    edge[cnt].w=len;
    edge[cnt].next=head[u];
    head[u]=cnt++;
}
void spfa(int u,int v,int n)
{
    memset(vis,0,sizeof(vis));
    memset(pre,INF,sizeof(pre));
    memset(dis,-INF,sizeof(dis));
    queue<int>q;

    q.push(u);
    dis[u]=0;
    vis[u]=1;
    while(!q.empty())
    {
        int x=q.front();
        q.pop();
        vis[x]=0;
        for(int p=head[x]; p!=-1; p=edge[p].next)
        {
            if(dis[edge[p].v]<dis[x]+edge[p].w||(dis[edge[p].v]==dis[x]+edge[p].w&&x<pre[edge[p].v]))
            {
                dis[edge[p].v]=dis[x]+edge[p].w;
                pre[edge[p].v]=x;
                if(!vis[edge[p].v])
                {
                    vis[edge[p].v]=1;
                    q.push(edge[p].v);
                }
            }
        }
    }

    int k=0;
    printf("%d\n",dis[v]);
    for(int i=v; i!=INF; i=pre[i])
        pre1[k++]=i;
    for(int i=1; i<k; i++)
        printf("%d %d\n",pre1[i-1],pre1[i]);
}
int main()
{

    while(~scanf("%d %d",&n,&m))
    {
        int u,v,w;
        memset(head,-1,sizeof(head));
        memset(id,0,sizeof(id));
        memset(cd,0,sizeof(cd));
        while(m--)
        {
            scanf("%d %d %d",&u,&v,&w);
            add(v,u,w);
            id[u]++;
            cd[v]++;
        }
        for(int i=1; i<=n; i++)
        {
            if(id[i]==0)
                u=i;
            if(cd[i]==0)
                v=i;
        }
        spfa(u,v,n);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值