较快速的最大带权独立集算法

前言

重新来吧 别输在过去最得意的事上。

只是单纯的记录一下这个小知识点。

很多时候,题目可以转化为求最大带权最小点覆盖或者是最大独立集。

但是他又经常把这个范围给到 n40 这种看上去可以用指数又不太能用指数的情况。

可能这个时候你就需要用到短小精悍的它。

基于状压和折半的独立集算法

首先给出我开始弄明白这个小知识点的来源题目:CF1767E Algebra Flash

题意是比较清楚的。

而你要从起点到达终点的一个充分必要条件是:

  • 必须选择起点和终点。

  • 相邻两个点必须选择一个点。

故你考虑对于起点和终点的颜色连一个自环,然后将相邻两个点分别对应的颜色连一条边。

故这个题就转化成了一个一般图最小带权点覆盖的问题。

而我们知道最小带权点覆盖 = 点权和 最大带权独立集。(注意这个东西在一般图上也是存在的

故我们只需要知道这个图的最大带权独立集是什么。

首先有一个很 naive 的状压是定义 dpS 表示只选择 S 内部的点可以构成的最大独立集是多少。

另外,我们定义点 x 的邻域的点构成的集合是 N(x)

转移的话本质上我们只需要考虑这个集合中的任意一个点就行了,为了方便我们后面的优化,考虑 highbit(S),假设是 u

显然有:dpS=max(dpSu,dpSN(u)u+wu(uN(u)))。(显然如果是自环的话你不能把这个点当作最大独立集上的点)

时间复杂度 O(2n)

但是这里 n40 显然不可过啊。

于是我们考虑一个奇技淫巧,也是这个算法的关键所在。

首先先把这个 dp 搬到记忆化搜索上面。由于我们使用的是 highbit(S),故这个 S 在搜素过程中逐渐减小。故我们考虑只对 s<2n2 这部分进行记忆化,而剩下的部分直接爆搜,具体见下:

long long dfs(long long sta)
{
    if(sta <= lim && ~dp[sta]) return dp[sta];
    if(sta == 0) return 0;
    int x = __lg(sta);
    long long ans = dfs(sta - (1ll << x));
    if(!((1ll << x) & d[x + 1])) ans = max(ans, dfs(sta - (1ll << x) - (sta & d[x + 1])) + w[x + 1]);
    if(sta <= lim) return dp[sta] = ans;
    return ans;
}

时间复杂度我们简单的证明一下。

对于搜索的前 n2 层,每向下递归一次,最多只会变成两个,故前半是 O(2n2)

对于后面的 n2 层,显然都会被记忆化到,无论你前面有多少种状态,加起来仍然只有 2n2 级别。(就是在记忆化和非记忆化分层的哪些点只有 2n2 而他们分别向外扩展也只会扩展成 2n2 级别,有点像 Meet in the middle 那种感觉)

故总时间复杂度 O(2n2)

本题代码和更多细节见

后记

还有一个叫做 Bron-Kerbosch 的算法,笔者认为在 OI 中的实用性远不如文章中这一种,故略过。

参考资料:《浅谈信息学竞赛中的独立集问题》 - 学习笔记

缝缝补补,缺漏的知识点会补的完吗?

posted @   Saltyfish6  阅读(42)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
Document
点击右上角即可分享
微信分享提示