hiho_110week

question:

给定一张N个点的完全图,可以从任何一个点出发,同一个点可以经过多次。询问总路径长度不超过M的情况下,最多能够经过多少个点。【边长度都为1】


analyse:

一、常规解析

使用数据结构:

dist[][],dist[i][j]表示经过i个点后,最后停留在j所以经过的最短路径长度;edge[j][k]表示从点j到点k的边长

   dist[i][j] = Min{ dist[i - 1][k] + edge[j][k] | 1 <= k <= N, k != j }


初始化:

dist[0][1 .. N] = 0,单个边的边长为1


算法执行:
算法结束的条件是我们模拟到一个i值,dist[i][1 .. N]都大于M。并得到最后的结果i-1


时间复杂度:

O(MN^2) 太高


二、利用二分特性

改善依据:

在i没有达到结果之前,dist[i][]中总是至少存在一个小于等于M的值,check(i)函数可判dist[i][]是否存在一个小于等于M,可以将这段路径二分为两端


二分过程:

前面我们给了一个edge[][]数组,一般的理解为任意两个点之间的边距离,但其实这个数组还有另一种理解方式:edge[j][k]表示从j出发,经过1条边到达k的路径距离。

我们将edge[][]记为edge_1,我们可以通过edge_1来计算edge_2,edge_2[j][k]即表示从j出发,经过2条边到达k的路径距离,并且在此基础上可以计算出经过3条边的edge_3,经过4条边的edge_4...一直到edge_i。

在实现上可以利用edge[][][]


时间复杂度:

求edge的过程就已经达到O(N^3)


三、二进制拼凑

改善依据:

任何一个自然素都可以用01序列表示出来,同样edge[i]的i也可以分解成若干个2的幂之和


拼凑过程:

计算出这些edge[2^k],算出edge[i]


算法实现:

根据输入得到edge_1
将edge_1与edge_1进行add操作得到edge_2,将edge_2与edge_2进行add操作得到edge_4...将edge_2^t与edge_2^t进行add操作得到edge_2^(t+1),直至2^t>M
计算edge_i,将i分解为若干个2的幂之和,将对应的edge_2^pt进行add操作
将dist[0][]与edge_i进行计算,即可得到dist[i][]


时间复杂度:
该算法预处理的时间为O(N^3logM),每次计算edge_i的时间为O(N^3logi),最后计算dist[i][]的时间为O(N^2),判断dist[i][]的时间复杂度为O(N)。因此进行一次check(i)操作总的时间复杂度为O(N^3logM)。加上二分答案过程总算法的时间复杂度为O(N^3 (logM)^2)


四、优化

优化点:

在得到edge_2^pt的情况下,还可以把这题做的更简单,将时间复杂度降低至O(N^3logM)。


算法实现:

首先同样要计算出所有的edge_2^pt,并初始化dist[] = 0(注意此时没有i,只是一个长度为N的数组)。

接下来从edge_2^t开始枚举,若dist[]和edge_2^t进行add操作后,在dist数组存在一个不超过M的值,将ans加上2^t,继续枚举下一个edge_2^(t-1);否则先将dist[]数组还原,再枚举下一个edge_2^p(t-1)。

//伪代码
dist[] = 0
ans = 0
For i = t .. 0
    newDist[] = Infinite
    For j = 1 .. N
        For k = 1 .. N
            If (j not equal k and newDist[j] > dist[k] + edge_2^i[k][j]) Then
                newDist[j] = dist[k] + edge_2^i[k][j]
            End If
        End For
    End For
    If (check(newDist)) Then
        dist = newDist
        ans = ans + 2^i
    End If
End For


思考:

最后问题归结为完全背包问题的变形。

将经过的片段按照二进制进行拆分并放入背包,如果符合要求就放入,不符合就将其移除。






























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值