守矢的关键路径

关键路径:源点到汇点的最长路径

解释:对于一个工程可以看做一个有向无环图(AOE网),只有一些项目被完成,才可以推进其他项目,而有的项目需要多个前提项目被完成才可进行,所以项目进行的最早时间就是他前提项目中最晚完成的时间。而关键路径就是决定整个工程完成时间的路径,所以关键路径就是工程起点到中点的最长路。
(注*:有环的图是不存在关键路径一说的。)

解法:算出关键路径需要用到拓扑排序,从某个点的所有入度中找权值最大的边,这样就算出了这个点允许发生的最早发生的时间 {dis[u]=max(dis[u],dis[v]+cost)},而所允许的最晚发生时间则需要逆拓扑排序,从终点出发找到达起点的最短路{dis[u]=min(dis[u],dis[v]-cost)}。

若某个点所允许的最晚发生时间和最早发生时间相等,那这个点就是关键路径上的点,所有这样的点所组成的路径这条路径就是关键路径。

这里写图片描述

对于上图,

最早发生时间:dis【0】=0,dis【1】=a1=5,dis【2】=a3=5,dis【3】=a1+a4=5,
dis【4】=a3+a5=12,dis【5】=a1+a4+a7=14。

最晚发生时间:dis【5】=14,dis【4】=dis【5】-a8=13,dis【3】=dis【5】-a7=6,
dis【2】=dis【4】-a5=6,dis【1】=dis【3】-a4=5,dis【0】=dis【1】-5=0。

守矢的关键路径

守矢神社正在进行庞大的核工程。核工程有多个环节,比如采矿需要重金邀请荷取,插排需要找城管幽幽子盖章,重型搬运需要造非想天则……整个工程项目中的各个子工程之间的先后完成关系建立了一张拓扑图,其中一条边表示一条工程。
为了方便描述,我们假定有n个状态,状态之间由工程连接,接下来有m条工程描述,每条描述由u,v,w三个整数构成表示从u状态必须完成持续w时间的工程后才能进入v状态。
知道杜邦公司为什么大赚一笔吗?因为他们现提出了工程网络中的“关键路径”。现在帮助守矢神社,求他们工程网络从1状态进入n状态过程中的所有关键活动状态点的个数。
如果对关键路径不熟悉或者看不懂题目的同学,请自行搜索并学习关键路径。

输入格式:

第一行n,m。
接下來m行每行三個數。
具体内容如题目描述所述。

输出格式:

一个数表示答案。

样例输入:
4 4
1 2 3
2 4 2
1 3 2
3 4 3
样例输出:
4

数据范围:
n<=200,m<=1000
时间限制:
1s
空间限制:
256M

思路:就是求关键路径上有多少点,注意是从1开始到n。

由于是现学现卖,代码很难看,如有不足请指出。

题解:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn=15000+10;
struct cc{
    int from,to,cost;
}es[maxn];
struct cc2{
    int from,to,cost;
}es2[maxn];
int ru[maxn];
int ru2[maxn];
int first[maxn],next[maxn],dis[maxn];
int first2[maxn],next2[maxn],dis2[maxn];
bool vis[maxn];
bool vis2[maxn];
int tot=0;
void build(int ff,int tt,int pp)
{
    es[++tot]=(cc){ff,tt,pp};
    next[tot]=first[ff];
    first[ff]=tot;
}
int tot2=0;
void build2(int ff,int tt,int pp)
{
    es2[++tot2]=(cc2){ff,tt,pp};
    next2[tot2]=first2[ff];
    first2[ff]=tot2;
}
int n,m;
void TopoSort()//求各点最早发生时间
{
    for(int i=1;i<=n;i++)
    {
        if(!ru[i]&&!vis[i])
        {
            vis[i]=1;
            for(int j=first[i];j;j=next[j])
            {
                int u=es[j].to;
                dis[u]=max(dis[u],dis[i]+es[j].cost);
                if(!vis[u])
                {
                    ru[u]--;
                    if(!ru[u])
                    {
                        TopoSort();
                    }
                }
            }
        }
    }
}
void TopoSort2()//求各点最晚发生时间
{
    for(int i=n;i>=1;i--)
    {
        if(!ru2[i]&&!vis2[i])
        {
            vis2[i]=1;
            for(int j=first2[i];j;j=next2[j])
            {
                int u=es2[j].to;
                dis2[u]=min(dis2[u],dis2[i]-es2[j].cost);
                if(!vis2[u])
                {
                    ru2[u]--;
                    if(!ru2[u])
                    {
                        TopoSort2();
                    }
                }
            }
        }
    }
}
int main()
{
    memset(dis2,63,sizeof(dis2));
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        build(x,y,z);
        build2(y,x,z);
        ru[y]++;
        ru2[x]++;
    }
    TopoSort();
    dis2[n]=dis[n];
    TopoSort2();
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(dis[i]==dis2[i])//统计
        {
            ans++;
        }
    }
    printf("%d",ans); 
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值