hdu 5168 Legal path(DP,栈优化)

Legal path

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 360    Accepted Submission(s): 93


Problem Description
Given a directed graph ,every edge has a weight.
A legal route is defined as the list of edge, for every edge's weight should at least K greater than last edge's weight,if the last one exist. the length of a legal route is the total weight of the edge on it.
We should find the legal path from node 1 to node n of minimum length.
 

Input
There is a number T shows there are T test cases below. (T10)
For each test case , the first line contains three integers n,m,Kn,m means the number of nodes and the number of edges on the graph. In following there are mlines. Each line has three integer x,y,z. indicate that there is an edge frome x to y weighted z.

2n100,000
0m200,000
1K,z1,000,000,000
1x,yn
 

Output
For each case ,if there is no legal path from 1 to n, output -1 ,otherwise output the answer.
 

Sample Input
3 3 2 1 1 2 1 2 3 2 3 2 1 1 2 2 2 3 1 4 6 2 1 2 1 1 2 2 2 3 3 1 3 5 2 4 2 3 4 7
 

Sample Output
3 -1 11
题意:求从1~n号点最短的一条路径,要求每条路必须必前一条路大至少k

思路:队友写了发迪杰斯特拉但是查不出错。。看了官方题解着实觉得很巧妙。

这里贴一下官方题解:

虽然看起来是一道最短路模型的题目,但由于要求了走的边权要递增,就可以设计出动态规划的做法。

先将所有边按照边权排序,要找的就是边列表的一个子序列,使得子序列中所有边起点为1,终点为n,其他边起点终点相连,边权递增且差不小于K。就可以得到一个动态规划的做法,暴力是O(m^2)O(m2)的,而转移的过程每次只针对一个点的所有出边,可以给每个点维护一个随着上一条边的权值上升而总代价减小的栈,就可以把DP的复杂度降低为O(n+m)O(n+m),当然因为要排序,再带一个log也是没问题的。


其中,优化部分最为巧妙。把所有的边按照权值排序后,前边的边一定比后面的边权值小,然后我们在扫描边的同时更新<=edge[i].len-k的边即可,注意,因为后面的边一定不会小于当前边权值,所以及时更新没有坏处。

然后我们每次取得1~edge[i].u(第i条边起点)的最小值,加上当前权值就是现在所能得到的edge[i].v(第i条边终点)的最短距离,一直更新到最后就能得到最优值了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <set>
using namespace std;
#define N 100050
#define INF 9999999999999
struct Edge
{
    int u,v;
    long long len,d;
} edge[N*2];
long long dis[N];
set<long long>se[N];
bool cmp(Edge a,Edge b)
{
    return a.len<b.len;
}
int main()
{
    int T;
    int n,m;
    long long k;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d %lld",&n,&m,&k);
        for(int i=1; i<=n; i++)
        {
            dis[i]=INF;
            se[i].clear();
        }
        for(int i=1; i<=m; i++)
        {
            scanf("%d %d %lld",&edge[i].u,&edge[i].v,&edge[i].len);
            if(edge[i].u==1)
            {
                edge[i].d=edge[i].len;
                dis[edge[i].v]=min(dis[edge[i].v],edge[i].d);
            }
            else edge[i].d=INF;
        }
        sort(edge+1,edge+1+m,cmp);
        int j=1;
        for(int i=1; i<=m; i++)
        {
            while(j<i&&edge[j].len+k<=edge[i].len)
            {
                se[edge[j].v].insert(edge[j].d);
                j++;
            }
            if(!se[edge[i].u].empty())
                edge[i].d=min(edge[i].d,*se[edge[i].u].begin()+edge[i].len);
            dis[edge[i].v]=min(dis[edge[i].v],edge[i].d);
        }
        if(dis[n]==INF) printf("-1\n");
        else printf("%lld\n",dis[n]);
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值