讲评个模拟赛差点把我整懵了……POJ3362 通信线路(最短路+二分答案)

给学弟办一场模拟赛,结果差点把自己给讲懵了……
于是决定记录下来,顺便理一理思路
说实话,这一题挺有价值的

题面

在郊区有 N N N 座通信基站, P P P 条双向电缆,第 i i i 条电缆连接基站 a i a_i ai b i b_i bi 。特别地, 1 1 1 号基站是通信公司的总站, N N N 号基站位于一座农场当中。现在,农场主希望对通信线路进行升级,其中升级第 i i i 条电缆需要花费 L i L_i Li

电话公司正在举行优惠活动。农场主可以指定一条从 1 1 1 号基站到 N N N 号基站的路径,并指定路径上不超过 K K K 条电缆,由电话公司免费提供升级服务。农场主只需要支付在该路径上剩余的电缆中,升级价格最贵的那条电缆的花费即可。求至少用多少钱能完成升级。

这一题同时也是 POJ3662,原题链接戳这里

初步思考——证明答案的单调性

选择的 K K K 条边一定是花费最多的 K K K 条边,这是十分显然的。

接着我们要明确一点,这道题的答案(即最终花费)是具有单调性的

怎么证明呢?

不妨考虑一下,如果选择一条边 e e e 的权值作为最终花费是不可行的,那它应该满足什么条件呢?

如果从图中任意选取一条从 1 1 1 N N N 的路径,记这个路径上所有边组成的集合为 E E E。如果 e ∈ E e \in E eE e e e E E E 中花费第 K + 2 ∼ N − 1 K + 2 \sim N-1 K+2N1 大的边,那么 e e e 的权值就一定不可以作为最终的答案(注意要对任何路径都成立)。因为即便你指定了 E E E 中权值前 K K K 大的为免费的边, e e e 依然不可能是剩余边中权值最大的边,总会有边比它的权值大。

那么,如果一条边 e 1 e_1 e1 的值 v 1 v_1 v1 不可能作为最终花费,那么任何比它小的边 e 2 e_2 e2 的权值 v 2 v_2 v2 都不可能作为最终答案。

B e c a u s e Because Because e 1 e1 e1 已经不可能在任意的 E E E 中权值排名前 K + 1 K + 1 K+1 大了,比 v 1 v_1 v1 的权值还要小的 e 2 e_2 e2 更加不可能排名前 K + 1 K + 1 K+1 大,也就不可能成为剩余的边中的最大花费。

至此,答案的单调性得证。

既然知道了答案是具有单调性的,那么我们就可以二分答案了。

转化——判定性问题

经过一系列的思考,我们将求解问题转化成了一个更易于求解的判定问题:

是否存在一种升级方案,使得剩余的电缆中,升级价格最贵的那条电缆的花费不超过 v a l val val

先给出二分的代码:(浅显易懂)

l = 0; r = maxw;
while (l < r) { // 答案具有单调性,所以进行二分
	mid = (l + r) >> 1;
	if (k < check(mid)) l = mid + 1; // 必须要选择的边数超过了k
	else r = mid;
}
printf("%d\n", l);

判定——01赋值法

若我们经过了一条花费大于 v a l val val 的电缆,则肯定要让电话公司对其进行升级;若我们经过了一条花费小于等于 v a l val val 的电缆,则不需要对其升级,把它算到路径上的 K K K 条电缆中即可。

于是我们可以将花费大于 v a l val val 的电缆看作是一条长度为 1 1 1 的边,将花费小于等于 v a l val val 的电缆看作是一条长度为 0 0 0 的边。用这种权值方式跑一遍 Dijkstra。若在这种计算方式下, 1 1 1 N N N 的最短路不超过 K K K,则存在一种升级方案,使得总花费不超过 v a l val val;反之则不存在一种升级方案,使得总花费不超过 v a l val val

以下是判定合法性的代码:(简洁明了)

inline int check(int ans) {
	fill(dis + 2, dis + 1 + n, 0x3f3f3f3f);
	fill(vis + 1, vis + 1 + n, 0);
    dis[1] = 0; // 保证第一次是取1号为起点
    for (int i = 1; i <= n; i++) {
        minn = 0x3f3f3f3f;
        beg = 0;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && dis[j] < minn) { //!vis[j]: 还没有被作为起点过
                minn = dis[j];
                beg = j;
            }
        }
        if (beg == 0) // 如果没有找到起点
            break;
        vis[beg] = 1;
        for (int j = head[beg]; j; j = e[j].next) {
            int y = e[j].to;
            int p = (e[j].w > ans ? 1 : 0); // 重新计算边权
            if (!vis[y] && p + dis[beg] < dis[y]) {
                dis[y] = p + dis[beg];
            }
        }
    }
    return dis[n];
}

时间复杂度 O ( ( N + P ) log ⁡ ⁡ M a x w ) O((N+P) \log⁡ Maxw) O((N+P)logMaxw),其中 M a x w Maxw Maxw 为花费最大的电缆的花费。

结语

主要是证明答案的单调性挺难给出严谨证明的……(或许是我太菜了)当然,意会一下其实也能知道

01赋值法那一块比较考察思想

其他就没什么了
在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值