[TopCoder 13001] BigO 题解

[TopCoder 13001] BigO 题解

题目描述

给定一张有向图,当 L 趋近于无穷大时,长度为 L 的路径条数有 S 条,此时若 S=O(Lk),输出 k,否则如果没有多项式的大 O 表示法,输出 1

指数情况

首先如果一张图中存在如下强连通分量,则 S=O(2L)

image

因为每次到 1 号点的时候都有两种选择,一种是直接回到 2,另一种是通过 3 绕回 2,从 2 回到 1 只有一种可能,所以按大 O 表示法,忽略常数,S=O(2L)

大胆猜测,对于一个 SCC,如果它不恰好为一个环,则它的路径条数是指数级别的。

以下给出证明:

image

如果一个 SCC 不是环,那么它的环上一定有一条伸出去的边,这条边连到的节点如果不在环上,那么一定能够找到一条路径回到 SCC,这条路径就形成了上图中的 ab

如果从 b 出发,有两种路径回来,一个是经过 ... 所代表的路径,一个是经过 234 所代表的路径,它们的长度都是 O(n) 级别的,那么长度为 L 的路径条数就是 O(2Ln) 级别的,省略掉 n,就是 O(2L) 级别的。

多项式情况

分类讨论以下

O(L0)

如果图中只有单个的环,那么只有一种走法就是一直绕,所以路径条数是常数条的,可以写作 O(L0)

O(L)

如果一个环 A 指向了另一个环 B,那么如果从环 A 出发,我们可以选择绕不超过 L|A| 次环 A 再走到环 B,对于 L 而言,|A| 是常数,所以最后路径条数就是 O(L) 的。

O(Lk)

这种情况出现当且仅当存在一条链,并且链上 SCC 大小 >1 的 SCC 个数为 k

可以感性理解,类比上述情况。

综上所述

SCC 缩点之后,一定会形成一张 DAG,这个 DAG 上有很多条链,而多条链互相是不会影响的,由于大 O 表示法只保留最大的指数,所以最终答案就是 DAG 上的最长链长度,注意这里长度的定义是链上 SCC 大小 >1 的 SCC 个数,因为单点是不会产生贡献的。

C++ 实现

注意 TopCoder 的题目需要写到一个指定的类里面。

时间复杂度:O(n+m)

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int N = 50 + 10;

vector<int> g[N], ig[N];
int n;
void init(vector<string> G) {
    n = G.size();
    for(int i = 0; i < n; i ++)
        for(int j = 0; j < n; j ++)
            if(G[i][j] == 'Y')
                g[i + 1].push_back(j + 1);
}

int dfn[N], low[N], timestamp, stk[N], in_stk[N], scc_cnt, sz[N], id[N], top, cnt[N];
void tarjan(int p)
{
    dfn[p] = low[p] = ++ timestamp;
    stk[++ top] = p, in_stk[p]= true;
    for(auto j : g[p]) {
        if(!dfn[j]) tarjan(j), low[p] = min(low[p], low[j]);
        else if(in_stk[j]) low[p] = min(low[p], dfn[j]);
    }
    if(dfn[p] == low[p]) {
        int y;
        scc_cnt ++;
        while(y != p) {
            y = stk[top --];
            in_stk[y] = 0, id[y] = scc_cnt, sz[scc_cnt] ++;
        } 
    }
}

int f[N];

int work() {
    for(int i = 1; i <= n; i ++)
        if(!dfn[i]) tarjan(i);
    for(int i = 1; i <= n; i ++)
        for(auto j : g[i]) {
            if(id[i] == id[j])
                cnt[id[i]] ++;
            else 
                ig[id[i]].push_back(id[j]);
        }
    for(int i = 1; i <= scc_cnt; i ++)
        if(cnt[i] > sz[i])
            return -1;
    int ans = 0;
    memset(f, -0x3f, sizeof f);
    for(int i = 1; i <= scc_cnt; i ++) {
        f[i] = -1;
        for(auto j : ig[i])
            f[i] = max(f[i], f[j]);
        f[i] += (sz[i] > 1);
        ans = max(ans, f[i]);
    }
    return ans;
}

class BigO {
    public :
        int minK(vector<string> graph) {
            init(graph);
            return work();
        }
} ;
posted @   MoyouSayuki  阅读(30)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
:name :name
点击右上角即可分享
微信分享提示