Problem 2 慢跑问题

背景
话说WZOI 的MWH 十分爱好运动,就在这NOIP 即将到来的日子里,也要坚持每天早上 去锻炼。。。
问题描述
这一天MWH 到附近的山上去慢跑,这座山上有N 个凉亭,简单地编号为1 到N(山顶的 凉亭编号为N,山底的凉亭编号为1)。有趣的是,如果凉亭i 和凉亭j 满足i 的海拔高度就大于凉亭i 的海拔高度。 这些凉亭中存在一些道路,连接两个海拔不同的凉亭,穿过一条道路需要花一定的时间。 但这里还有一个十分诡异的事件,这里存在一些特殊道路。从这些道路经过MWH 可以回到 过去,也就是说MWH 需要花的时间为负值。通过这些道路,MWH 可以花较短的时间跑完 他需要跑的路程。这样他就可以节约下许多时间,回到机房刷水题。 MWH 想从凉亭1 跑到凉亭N 去,但他不想走下坡路,也就是说他想要走一条海拔递增的 路径,因为MWH 始终认为递增的东西是具有美感的。 MWH他想知道他最少需要花多少时间就可以从山底的凉亭到达山顶的凉亭?有一点需要 注意,MWH 到达山顶的凉亭的时间可能会小于他出发的时间, 这就意味着他穿越了时空。
这是一件多么美妙的事,于是MWH 也想知道他是否能穿越时空。 他把这个任务交给了你,他希望你能告诉他他最少需要花多少时间才能到达山顶,同时你 也需要告诉他,他是否能穿越时空。
输入格式
输入数据第一行包含三个整数N,ML,MD(分别用一个空格隔开),分别表示凉亭的数 目,普通道路的数目和特殊道路的数目;
第2 行到第ML+1 行,每行三个整数Ai,Bi,Ci(分别用一个空格隔开,1≤Ai,Bi≤N, Ai≠Bi),表示普通道路连接凉亭 Ai和 Bi,穿过这条道路需要花时间Ci;
第ML+2 行到第ML+MD+1 行,每行三个整数Ai ,Bi,Ci(分别用一个空格隔开,1≤Ai,Bi ≤N,Ai≠Bi),表示特殊道路连接凉亭 Ai 和 Bi,穿过这条道路会回到相对时间Ci 之前。
输出格式
输出数据包含两行。
第一行包含一个整数T,表示MWH 最少花多少时间能到达山顶的凉亭;
第二行包含一个字符串“YES”或“NO”(不包含引号),如果MWH 可以回到过去那么 输出“YES”,否则输出“NO”。
样例输入输出
Sample #1
jogging.in
4 3 2
1 2 1
1 3 4
3 4 3
2 4 1
1 4 2
jogging.out  
-2
YES
Sample #2
jogging.in 
5 4 2
2 4 2
2 3 3
3 5 4
4 5 1
1 2 2
1 3 1
jogging.out  
1
NO
数据规模
对于10%的数据,N≤100,0≤ML,MD≤1000,Ci≤100;
对于30%的数据,N≤10000,0≤ML,MD≤30000,Ci≤500;
对于100%的数据,N≤100000,0≤ML,MD≤1000000,Ci≤2000。
输入数据保证从1 到N 会有一条路径。
时间限制
1s
提示
输入格式中的相对时间指:如果到达这条道路的出发点时间为T,那么穿过这条道路后的 时间为T-Ci。  


分析:

题目大意:给出一个具有N 的顶点,M 条边的图,要求输出点1 到N 的最短路。
考察算法:有向无环图的单源最短路
算法一  
根据题目的要求我们可以构建出一个有向图G,然后我们在这个图上一边 SPFA 就可以求出点1 到点N 的最短路。注意这道题目不能使用Dijkstra 算法, 因为我们会发现这个图出现了负权,这使得用贪心算法实现的Dijkstra 算法没 有了用武之地。
时间复杂度:O(kM) 空间复杂度:O(N+M) 期望得分:30 分
算法二  
仔细分析一下题目我们可以发现这个有向图G,其实是一个有向无环图。对于 有向无环图我们有一个十分高效方法来求得单源最短路,其基本思想是DP。 具体实现过程可以见下面的附录,那里又十分详细的介绍。
时间复杂度:O(N+M) 空间复杂度:O(N+M) 期望得分:100 分  

#include
#include
#include
#include
using namespace std;
const int maxn=100005,maxm=2000005,INF=2000000000;
int dis[maxn],head[maxn],n,ml,md,cnt=0;
struct eadge
{
    int next,to,dist;
} e[maxm];
void add(int from,int to,int dist)
{
    e[++cnt].next=head[from];
    e[cnt].to=to;
    e[cnt].dist=dist;
    head[from]=cnt;
}
int main()
{
    freopen("jogging.in","r",stdin);
    freopen("jogging.out","w",stdout);
    scanf("%d%d%d",&n,&ml,&md);
    int x,y,z;
    for(int i=1;i<=ml;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        if(xdis[i]+e[j].dist)
                    dis[to]=dis[i]+e[j].dist;
            }
        }
    }
    printf("%d\n",dis[n]);
    if(dis[n]<0) printf("YES\n");
        else printf("NO\n");
    fclose(stdin);
    fclose(stdout);
    return 0;
}

当然用直接用spfa也是不会错的,但要比上一种算法要慢一点。
关于慢跑问题的说明:
这道题目主要为了考察有向无环图的单源最短路。下面来详细介绍着这种算法:
适用条件和范围:
1) DAG(Directed Acyclic Graph,有向无环图);
2) 边权可正可负。
算法描述:
1) Toposort
2) If Toposort=False Then HALT(Not a DAG)
3) For 拓扑序的每个顶点u do
For u的每个邻接点v do
Relax(u,v,w);
算法结束后:如有环则输出错误信息;否则dis[i]为s到i的最短距离,pre[i]
为前驱顶点。
算法补充:
此算法时间复杂度O(V+E),时间和编程复杂度低,如遇到符合条件的题目(DAG),推 荐使用。还有,此算法的步骤可以在Toposort中实现,这样即减小了此算法复杂度的一 个系数。
其实这道题目还有许多地方需要注意:
1) 输入数据给出的图并不是有向图,当然更不是有向无环图,我们需要根据题目隐藏 的条件将这个图构建成有向无环图;
2) 题目已经说明一定要从编号小的凉亭跑到编号大的凉亭,这就意味这我们完全不需 要进行拓扑排序,因为数据关系已经具有拓扑关系了。我们只需要从1到N进行DP 即可。   



你可能感兴趣的:(动态规划,模拟赛,最短路)