zoukankan      html  css  js  c++  java
  • 洛谷P5022 旅行(NOIP提高组2018 D2T1)题解 贪心/去环

    题目链接:https://www.luogu.com.cn/problem/P5022

    解题思路:

    这里最重要的是数据范围里面的 (m=n-1) 或者 (m=n)

    (m=n-1) 的时候是一棵树,我们按照从当前节点找编号最小的子节点的策略进行深搜就能够解决这个问题。

    (m=n) 的时候只存在一个环,我们只需要找到环上面的每一条边,然后枚举每一条边,在假设这条边被去掉的情况下(去掉一条环上面的边这个图就又变成了树)进行给予上述贪心思想的搜索。

    所以总的时间复杂度是 (O(n^2)) (因为这里 (n)(m) 最多相差 (1),所以这么说)。

    实现起来要注意一些袭击,比如邻接表最好一遍排序,接下来就用就可以了。

    实现代码如下(略显繁琐):

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 5050;
    struct Edge {
        int u, v, nxt;
        Edge() {};
        Edge(int _u, int _v, int _nxt) { u = _u; v = _v; nxt = _nxt; }
    } edge[maxn<<1];
    int n, m, ecnt, head[maxn];
    void init() {
        ecnt = 0;
        memset(head, -1, sizeof(int)*(n+1));
    }
    void addedge(int u, int v) {
        edge[ecnt] = Edge(u, v, head[u]); head[u] = ecnt ++;
        edge[ecnt] = Edge(v, u, head[v]); head[v] = ecnt ++;
    }
    bool flag[maxn<<1], vise[maxn<<1];
    int fe[maxn<<1], dep[maxn];
    void dfs1(int u, int d) {   // 用来构造一棵树,从而找到没有使用的那条边
        dep[u] = d;
        for (int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].v;
            if (!dep[v]) {
                dep[v] = dep[u] + 1;
                vise[i] = vise[i^1] = true;
                fe[v] = i^1;
                dfs1(v, d+1);
            }
        }
    }
    void after_dfs1() { // 标记所有在环上的边
        int u, v;
        for (int i = 0; i < ecnt; i += 2) {
            if (!vise[i]) {
                flag[i] = flag[i^1] = true;
                u = edge[i].u;
                v = edge[i].v;
                break;
            }
        }
        while (u != v) {
            if (dep[u] < dep[v]) swap(u, v);
            int i = fe[u];
            flag[i] = flag[i^1] = true;
            u = edge[i].v;
        }
    }
    vector<int> g[maxn];
    int gsz[maxn];
    int ans[maxn], tmp[maxn], tmpcnt;
    void before_dfs2() {
        for (int u = 1; u <= n; u ++) {
            for (int i = head[u]; i != -1; i = edge[i].nxt) {
                int v = edge[i].v;
                g[u].push_back(v);
            }
            sort(g[u].begin(), g[u].end());
            gsz[u] = g[u].size();
        }
    }
    void dfs2(int u, int p, int ii) {
        // printf("dfs2 : u = %d , p = %d , ii = %d
    ", u, p, ii);
        tmp[tmpcnt++] = u;
        int sz = gsz[u];
        // printf("	g[%d].size() == %d
    ", u, sz);
        for (int i = 0; i < sz; i ++) {
            int v = g[u][i];
            if (v != p && !(u == edge[ii].u && v == edge[ii].v) && !(u == edge[ii].v && v == edge[ii].u))
                dfs2(v, u, ii);
        }
    }
    int main() {
        scanf("%d%d", &n, &m);
        init();
        for (int i = 0; i < m; i ++) {
            int u, v;
            scanf("%d%d", &u, &v);
            addedge(u, v);
        }
        before_dfs2();
        if (m == n-1) { // 本身就是一棵树
            dfs2(1, -1, -1);
            for (int i = 0; i < n; i ++) printf("%d ", tmp[i]);
        }
        else {  // 存在一个环
            dfs1(1, 1);
            after_dfs1();
            bool first = true;
            for (int i = 0; i < ecnt; i += 2) {
                if (flag[i]) {
                    tmpcnt = 0;
                    dfs2(1, -1, i);
                    bool flag = false;
                    if (first) {
                        flag = true;
                    }
                    else {
                        for (int i = 0; i < n; i ++) {
                            if (ans[i] != tmp[i]) {
                                if (ans[i] > tmp[i]) flag = true;
                                break;
                            }
                        }
                    }
                    if (flag) {
                        if (first) {
                            first = false;
                        }
                        for (int i = 0; i < n; i ++) ans[i] = tmp[i];
                    }
                }
            }
            for (int i = 0; i < n; i ++) printf("%d ", ans[i]);
        }
        return 0;
    }
    
  • 相关阅读:
    C++中重载,重写,隐藏的区别
    以太网(局域网)交换机工作原理
    IP地址、MAC地址、ARP地址解析协议
    Metasploitable渗透测试实战——Windows漏洞 MS08-067复现
    一次对真实网站的SQL注入———SQLmap使用
    多字节与宽字节转换
    char*、string、CString各种字符串之间转换
    国密SM4分组加密算法实现 (C++)
    网络编程——基于UDP的网络化CPU性能检测
    U盘小偷——C++实现U盘插入检测和文件扫描拷贝
  • 原文地址:https://www.cnblogs.com/quanjun/p/13149563.html
Copyright © 2011-2022 走看看