TC SRM 684 Div2 900pts

题意:

给定一个有向图,可能会有重边和自环。问是否会有一条路径使得图中每个点都至少被访问一次。 n50

解释:

其实这题并不难,因为数据范围较小,所以可以有很多方法暴力解决!
看了一下TC的官方题解,感觉学到了一波姿势!

第一点 : 首先肯定是要缩点,一般是Tarjan咯!
题解是提供了一种 N3 的缩点的方法:
用邻接矩阵表示这个图,然后 Floyd 处理出任意两个点是否可达。
然后就可以达到缩点的目的了!

第二点:缩点后得到一个 DAG,然后判断是否可以一条路径覆盖所有的点,这个可以用 最小路径覆盖解决,也就是经典的二分图匹配。
题解给出的是 topsort :
按拓扑排序的方法去选出路径上的每个点,最后判断一下选出的点的序列中相邻的两个点之间是否存在可达就行了!

思考:这种方法确实可以去解决这种是否存在一条路径覆盖所有点的问题,而且效率是高于二分图匹配的,但是这种方法是否可以代替用二分图匹配去解决最小路径覆盖的问题呢?

也就是说,我们先按拓扑排序的方法得到选出的点的序列,然后判断这个序列中相邻的点 u,v ,是否满足 reach[u][v]==1 ,也就是是否可达,如果不满足就 ans++ ,这样得到最终的最小路径覆盖!这样的做法是否正确呢?

代码:
string check(vector<int> z0, vector<int> z1)
{
    // create the graph from input, adjacency matrix:
    int n = z0.size();
    vector< vector<int> > a(n, vector<int>(n,0) );

    for (int i = 0; i < n; i++) {
        a[i][z0[i]] = 1;
        a[i][z1[i]] = 1;
    }

    // Floyd-Warshall to quickly get which vertices are reachable from which starting points:
    auto d = a;
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                d[i][j] = (d[i][j] || (d[i][k] && d[k][j])  ); 
            }
        }
    }

    // if a vertex u is reachable from vertex v and vice versa, then they
    // belong to the same SCC, use that information to build a SCC graph:
    vector<int> node_compo(n, -1);
    int t = 0;
    for (int i = 0; i < n; i++) {
        if (node_compo[i] == -1) {
            node_compo[i] = t++;
            for (int j = 0; j < n; j++) {
                if ( d[i][j] && d[j][i] ) {
                    node_compo[j] = node_compo[i];
                }
            }
        }
    }

    vector< set<int> > g(t) , rg(t);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            int u = node_compo[i];
            int v = node_compo[j];
            if ( (u != v) && a[i][j] ) {
                g[u].insert(v);
                rg[v].insert(u);
            }
        }
    }

    // topsort the SCC graph:
    vector<int> top_sort(t);
    vector<bool> added(t, false); 
    for (int i = 0; i < t; i++) {
        int pick = -1;
        for (int j = 0; j < t; j++) {
            if ( ! added[j] ) {
                int cnt = 0;
                for (int k : rg[j]) {
                    if ( !added[k] ) {
                        cnt++;
                    }
                }
                if (cnt == 0) {
                    pick = j;
                }
            }
        }
        if ( pick == -1) {
            return "Does not exist";
        } else {
            top_sort[i] = pick;
            added[pick] = true;
            cout << "[";
            for (int i = 0; i < n; i++) {
                if (node_compo[i] == pick) {
                    cout << i << " ";
                }
            }
            cout << "]" << endl;
        }
    }

    if (top_sort[0] != 0) {
        return "Does not exist";
    }
    // check the path:
    for (int i = 1; i < t; i++) {
        if ( g[ top_sort[i-1] ].count( top_sort[i]) == 0) {
            return "Does not exist";
        }
    }
    return "Exists";
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值