厦门大学“网宿杯“17届程序设计竞赛 E-芜湖起飞

芜湖起飞

https://ac.nowcoder.com/acm/contest/5945/E

单源最短路径、三分、思维

今天的我又被狠狠地教训了。。。。嘿嘿。。这道题再次告诉我细心是件多么重要的事情!!!!

题意:
安徽芜湖有n个机场,一共有m条线路在空管部门报备。
每条线路单向连接两个机场,并且需要的通行时间每天都可能不一样。
具体来说,设目前是第x天,那么第i条线路所需要的通行时间为k*x+b
一年一共有H天,也就是说,x取[0,H]中的整数。
现在大司马想从1号机场在一天内换乘任意多次航班前往n号机场,他总是选择用时最短的方式,
现在他想知道哪一天需要花最长的时间。

输入格式:
每个测试点仅包含一组输入数据。
第一行三个整数n,m,H(1<=n,m<=114514,1<=H<=1e9),表示机场的数量,线路的数量和x的取值范围。
接下来m行,每行四个整数u,v,k,b(1<=u,v<=n,-10^9<=k,b<=10^9)
表示有一条从u指向v的单向线路,该线路函数为kx+b
同一对机场之间可能有多条路径,且机场和自己之间也可能有路径。
保证在[0,H]的任何一天,每条航线的长度非负且不超过10……9,且从1号机场可以到达n号机场。

输出描述:
输出一行一个整数,表示最长的用时。

分析

很简单这和图论中的单源最短路径有关,毕竟我们要求编号1的点到编号n的点的最短距离嘛。
那关键麻烦的是,路径的长度是随时间变化的!!!
每天路径长都不同,这也意味着每天的最短路径的大小都不一样。
要求这些天中最大的最短路径大小。
如果我们从0到H每一天都遍历的话,将每一天的最短路径求出的话肯定是过不了的。
毕竟H最大1e9,m最大1e5,n最大1e5复杂度O(Hnlogm)。。。实在是太大了。
我们需要优化,能不能不要全部枚举。

假设我们在x天中选择了路径[e1,e2,e3,e4,e5]
通过这些路径从1到达了n,这些路径的k与b分别为k1,k2......k5 b1,b2...b5
那么最终路径和为k1x+b1 + k2x+b2 + k3x+b3 + k4x+b4 + k5x+b5
那么我们也可以这样表示:
(k1+k2+k3+k4+k5)
x+(b1+b2+b3+b4+b5)
简写为:kx+b !!!!!!
这是否给我们一点启示,我们应该能感觉到这就是解题的关键!!!
这样想好了,假设我们以上述的k
x+b的形式列出所有从1到n的路径(路径可能是无限的,但是这并不妨碍,我们就假设我们全部罗列出来了)
那么,我们得到了许许多多的一次函数:[k1x+b1,k2x+b2,k3x+b3,k4x+b4 ........ ]
随着时间即自变量x的变化y = k*x+b也会成直线变化
我们要求在所有x中最大的 最小kx+b。十分抱歉,口头不好表述。希望能理解我的意思。
将这些函数画在坐标系中,我们会发现图中的黑点,其实就是我们所求的答案。
图片说明
最下方轮廓其实就是意味着每一个时间点x对应的最短路径
这个黑点位于最下方轮廓的最高点,
我们会发现最下方轮廓会呈现一个先增再减的态势,
即我们的最小路径随着时间的增加会先增加到某一天后再减少。
那么,求这个函数的最高点问题就很熟悉了,三分法!!!!!!!
左边界为0,右边界为H。利用三分法我们可以很快的求解出来。

下面代码,注意细节

#include<iostream>;
#include<vector>;
#include<queue>;
#include<functional>;
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int max_m = 114520;
const ll INF = 3e18;
struct edge { ll to,k, b; };
vector<edge>G[max_m];
ll d[max_m];
ll V, m, h;

ll dijkstra(ll s,ll x) {
    priority_queue<P, vector<P>,greater<P>> que;
    fill(d + 1, d + V + 1, INF);
    d[s] = 0;
    que.push(P(0, s));
    while (!que.empty()) {
        P p = que.top();que.pop();
        int v = p.second;
        if (d[v] < p.first)continue;
        for (int i = 0;i < G[v].size();i++) {
            edge e = G[v][i];
            if (d[e.to] > d[v] + e.k * x + e.b) {
                d[e.to] = d[v] + e.k * x + e.b;
                que.push(P(d[e.to], e.to));
            }
        }
    }
    return d[V];
}

ll solve(ll l,ll r) {
    while (l+5 < r) {
        ll mid1 = (l + r) >> 1;
        ll mid2 = (r + mid1) >> 1;
        ll cmp1 = dijkstra(1,mid1);
        ll cmp2 = dijkstra(1,mid2);
        if (cmp1 < cmp2)l = mid1 + 1;
        else r = mid2 - 1;
    }
    ll ans = 0;
    for (int i = l;i <= r;i++)
        ans = max(dijkstra(1, i), ans);
    return ans;
}

int main() {
    ios::sync_with_stdio(0);
    cin >> V >> m >> h;
    ll u, v, k, b;
    for (int i = 1;i <= m;i++) {
        cin >> u >> v >> k >> b;
        G[u].push_back(edge{v,k,b});
    }
    cout << solve(0,h) << endl;
}

而我主要被数据范围给坑了,最终的答案是超过int的这点我很清楚,在放置距离的数组中也将数据
范围扩大至long long 了,但是在狄克斯特拉算法中,pair仍是用的pair<int,int>这直接导致我提交后
遇到大的数据类型后发生溢出,然后陷入死循环。。。。。。
而傻傻的我,还以为自己的三分法有问题,应是盯了半天,,,,,,,,
最后,比赛结束看到别人的AC代码时也没有反应过来还以为自己被针对了。。。。。。。嘿嘿。。。。唉

全部评论

相关推荐

hanliu:1. 排版与格式问题字体与对齐问题:标题和内容的字体大小差异不够明显,无法迅速吸引目光。某些文字看起来有些拥挤(比如校园经历中的“班委成员”部分)。2. 内容逻辑性模块顺序问题:实习经历放在较靠后的位置,实际上这部分内容对应聘来说更重要,建议提前突出。细节表述不够突出:比如教育背景部分的专业课程仅仅列出名字,没有说明自己在这些课程中表现如何或者掌握了什么技能,缺乏量化描述。多余内容:例如“班委成员”和“宣传委员”这类校园经历,叙述过于普通,缺乏和岗位相关的实质性贡献。,建议简写。3. 措辞专业性表达不够精准:例如“协助班长与团支书更好地为同学服务”显得较为笼统,没有实际成果的体现。用词重复:如“学习了焊接”“学习了光检”等重复词语较多,缺乏丰富的动词来展示个人能力(如“负责”“优化”“改进”等)。技能展示不足:虽然列出了UG和CAD证书,但没有明确提到这些技能如何在实际工作中发挥作用。4. 技能匹配度技能深度不足:虽然列出了掌握的软件和技术,但没有描述技能水平(如“熟练掌握”“精通”),也没有具体案例支持这些技能。缺乏岗位导向性:比如针对机械设计与制造方向,实习经历提到了“E6尾灯项目”,但没有详细说明自己在其中的技术贡献,可能会显得经验描述泛泛而谈。5. 自我评价问题表达空泛:如“具有良好的沟通协调能力”“责任心强”之类的描述太常见,没有让人眼前一亮的特点。缺乏成果支持:自我评价中的能力没有用具体项目、经历或成就来验证,可信度较弱。 兄弟加油
点赞 评论 收藏
分享
评论
5
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务