zoukankan      html  css  js  c++  java
  • CF-125E MST Company (单度限制最小生成树)

    参考红宝书

    题目链接

    对除 1 号点顶点外的点集,求一次最小生成森林,对于最小生成森林的联通分量,选择最短的一条边与 1 号点相连。设此时 1 号点的度为 (k_0),如果 (k_0lt L) 则无解 (L为1号顶点的规定度)

    然后通过可行交换来增加 1 号点的度,每次尝试加入一条和 1 号点相连的边,然后删去所形成的环上面的最长边。

    此题点数为 5000,对于每次交换,可以用树形DP求出所有点到 1 号点的最长边。每次选择增量最小的边去交换,直到 (k_0) 达到 L

    在实现中的一些困难:

    1. 答案要求构成生成树的边序号,所以加边时要保留原边序号信息
    2. 树形DP要找到每个点到 根 的最长路大小,以及对应边的序号
    3. 如果一个点与根直接相连,可以把这个边序号保留下来(下面代码中用path数组保留),方便之后做替换用
    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int inf = 0x3f3f3f3f;
    #define dbg(x...) do { cout << "33[32;1m" << #x <<" -> "; err(x); } while (0)
    void err() { cout << "33[39;0m" << endl; }
    template<class T, class... Ts> void err(const T& arg,const Ts&... args) { cout << arg << " "; err(args...); }
    const int N = 5000 + 5;
    const int M = 200010;
    int head[N], ver[M], nxt[M], edge[M];
    int fa[N], mark[M], best[N], cand[N];
    int path[N];
    int n, m, k, tot;
    struct Edge{
        int x, y, z;
        int id;
        Edge(int a=0,int b=0, int c=0, int d=0):x(a),y(b),z(c),id(d){}
        bool operator <(Edge b){
            return z < b.z;
        }
    }e[M];
    void add(int x, int y, int z, int id){
        ver[id] = y, edge[id] = z, nxt[id] = head[x], head[x] = id;
    }
    void addEdge(int i){
        add(e[i].x, e[i].y, e[i].z, i*2);
        add(e[i].y, e[i].x, e[i].z, i*2+1);
    }
    int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
    void dfs(int x, int fat){
        for(int i=head[x];i;i=nxt[i]){
            if(!mark[i>>1] || ver[i] == fat) continue;
            int y = ver[i];
            if(x == 1){// 从根出发的边,不能被替换
                best[y] = -inf;
            } else {
                best[y] = edge[i], cand[y] = i >> 1; // 边的序号其实是 i/2
                if(best[y] < best[x]){
                    best[y] = best[x];
                    cand[y] = cand[x];
                }
            }
            dfs(y, x);
        }
    }
    bool solve(){
        sort(e + 1, e + 1 + m);
        for(int i=1;i<=n;i++) fa[i] = i;
        for(int i=1;i<=m;i++){
            int x = e[i].x, y = e[i].y;
            if(x == 1 || find(x) == find(y)) continue;
            mark[e[i].id] = 1;
            fa[find(x)] = find(y);
        }
        //按照id排序,便于之后处理
        sort(e + 1, e + 1 + m, [](Edge a, Edge b){return a.id < b.id;});
        int component = 0;//连通块个数
        for(int i=2;i<=n;i++){
            if(find(i) == i){
                component ++;
                best[i] = inf;
            }
        }
        if(component > k) return false;
        for(int i=1;i<=m;i++){
            if(e[i].x != 1) continue;
            path[e[i].y] = i;
            int rt = find(e[i].y);
            if(e[i].z < best[rt]){
                best[rt] = e[i].z;
                cand[rt] = i;
            }
        }
        for(int i=2;i<=n;i++){
            if(find(i) != i) continue;
            if(best[i] == inf) return false;
            mark[cand[i]] = 1;
        }
        for(int i=1;i<=m;i++){
            if(mark[i]) addEdge(i);
        }
        while(component < k){
            dfs(1, 0); //树形DP
            int mx = inf, tcand = 0;
            for(int i=2;i<=n;i++){
                if(path[i] == 0 || best[i] == -inf) continue;
                if(e[path[i]].z - best[i] < mx){
                    mx = e[path[i]].z - best[i];
                    tcand = i;
                }
            }
            if(mx == inf) return false;
            mark[cand[tcand]] = 0;
            mark[path[tcand]] = 1;
            addEdge(path[tcand]);
            component ++;
        }
        printf("%d
    ", n-1);
        for(int i=1;i<=m;i++){
            if(mark[i]) printf("%d ", i);
        }
        return true;
    }
    int main(){
        scanf("%d%d%d", &n, &m, &k);
        for(int i=1;i<=m;i++){
            int x, y, z;scanf("%d%d%d", &x, &y, &z);
            if(x > y) swap(x, y);
            e[i] = Edge(x, y, z, i);
        }
        if(!solve()) puts("-1");
        return 0;
    }
    
    
  • 相关阅读:
    使用定时器实现获取手机验证码倒计时
    搜索历史管理
    利用vue和jQuery实现中国主要城市搜索与选择
    使用vue、jQuery生成带有logo的二维码
    使用vue-cli脚手架搭建Vue项目
    postcss-px-to-viewport
    git命令操作篇
    小程序中live-player
    对于常用数组的方法总结
    css的加载中动画
  • 原文地址:https://www.cnblogs.com/1625--H/p/12767830.html
Copyright © 2011-2022 走看看