acwing 164. 可达性统计

地址 https://www.acwing.com/problem/content/description/166/

给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量。

输入格式
第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边。

输出格式
输出共N行,表示每个点能够到达的点的数量。

数据范围
1≤N,M≤30000
输入样例:
10 10
3 8
2 3
2 5
5 9
5 9
2 3
3 9
4 8
2 10
4 9
输出样例:
1
6
3
3
2
1
1
1
1
1

解答

算法1
(暴力枚举)
从暴力开始吧。
就是bfs遍历各个点 再将能达到的点能达到的点再次加入。
结果是毫无悬念的tle了。

在朴素的暴力枚举的时候 隐隐感觉使用拓扑排序后,BFS能有效的减少层级
而且反向搜索点的时候 后面搜索的点可以使用前面已经搜索了的点能达到的点记录
问题在于 使用已经搜索后的记录 会有重复的点,如何去重? 我使用了set
结果这种想法也tle了

(拓扑排序 压缩)
最后得出正确的解决方案是拓扑排序后使用bitset压缩
c++代码

#include <iostream>
#include <vector>
#include <queue>
#include <bitset>

using namespace std;

vector<int> g[30010];
vector<int> copyg[30010];
int dIn[30010];
bitset<30010> f[30010];
int n, m;
/*
10 10
3 8
2 3
2 5
5 9
5 9
2 3
3 9
4 8
2 10
4 9
*/

void solve()
{
    queue<int> q;
    int i = 1;
    for ( i = 1; i < n; i++) {
        if (dIn[i] == 0) {
            q.push(i);
        }
    }
    for (int i = 0; i < 30010; i++) {
        copyg[i] = g[i];
    }

    vector<int> ans;
    while (q.size()) {
        int point = q.front();
        q.pop();
        ans.push_back(point);

        //抹掉该点 和该点发出的边
        for (auto& e : g[point]) {
            //减掉入度
            dIn[e]--;
            //如果入度为0  可以放入队列 进行下一次遍历
            if (dIn[e] == 0)
                q.push(e);
        }

        g[point].clear();
    }

    for (int i = ans.size() - 1; i >= 0; i--) {
        int j = ans[i];
        f[j][j] = 1;
        for (int k = 0; k < copyg[j].size(); k++) {
            f[j] |= f[copyg[j][k]];
        }
    }

    for (int i = 1; i <= n; i++) {
        cout << f[i].count() << endl;
    }
}

int main()
{
    cin >> n >> m;
    for (int i = 0; i < m; i++) {
        int a, b; cin >> a >> b;
        g[a].push_back(b);
        dIn[b]++;
    }

    solve();


}

作者:itdef
链接:https://www.acwing.com/file_system/file/content/whole/index/content/585665/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

acwing 164. 可达性统计

上一篇:利用虚拟技术解决WIN7兼容问题


下一篇:windows server 2012 端口访问失败