zoukankan      html  css  js  c++  java
  • CF125E MST Company

    题面

    有一张 (n) 个点,(m) 条边的图,每条边有边权。需要找出一棵生成树,使得 1 号点度数恰好为 (k) ,在满足这个条件的前提下生成树的权值和尽量小。

    无解输出 −1,否则任意输出一种方案即可

    (1 ≤ n ≤ 5000,0 ≤ m ≤ 100000,0 ≤ k ≤ 5000)

    solution

    收获蛮大的一个题

    两种做法,但其中一种好像假掉了

    • 破圈

    很经典的一种套路,和 std 跑了一上午对拍,为啥我的还是过不了

    思路是这样的:

    首先把与一号点所连的边全部都删掉,用其他边生成一个森林。

    如果生成的联通块的数目 (x > k) 显然无解

    如果联通块数目 (x = k) ,把与一号点相连的边从小到大排个序,然后用它们把所有联通块连起来,形成的树就是答案

    如果联通块的数目 (x < k) ,同上,先生成一棵树,此时 (1) 的度数小于 (k) ,考虑与一号点相连的非树边,每次向树中加一条这样的边就会形成一个环,找出环上边权最大的一条边 (不与一号点相连) 删掉,这样 1 的度数就会 + 1,重复这个操作 (k - x) 就会保证 1 的度数恰好为 k 了

    这个做法细节蛮多的:

    怎么找环上最大的边:(DFS)​ ,边权转到点上处理

    时间复杂度:

    (O (nk imes mlogm))

    /*
    work by:Ariel_
    */
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <queue>
    #include <vector>
    #include <algorithm>
    #define ll long long
    #define rg register
    using namespace std;
    const int M = 1e5 + 5;
    const int N = 5000 + 5;
    int read(){
        int x = 0,f = 1; char c = getchar();
        while(c < '0'||c > '9') {if(c == '-') f = -1; c = getchar();}
        while(c >= '0' && c <= '9') {x = x*10 + c - '0'; c = getchar();}
        return x*f;
    }
    vector<int> T, vec;
    int n, m, k, fa[N], cnt, sum, id[M << 1], del[M << 1], fag[M];
    ll mx[N];
    int find(int x) {return fa[x] == x ? fa[x] : find(fa[x]);}
    struct Edge{int u, v, w, id;}E[M];
    struct edge{int u, v, w, nxt;}e[M << 1];
    int Ecnt, head[N];
    void add_edge(int u, int v, int w) {
        e[++Ecnt] = (edge) {u, v, w, head[u]};
        head[u] = Ecnt;
    }
    bool cmp(Edge x, Edge y) {return x.w < y.w;}
    void forest() {
    	for (int i = 1; i <= n; i++) fa[i] = i;
    	sort(E + 1, E + m + 1, cmp);
        for (int i = 1; i <= m; i++) {//生成森林 
            int x = find(E[i].u), y = find(E[i].v);
            if (x == y || x == 1 || y == 1) continue; 
            fa[x] = y; T.push_back(E[i].id);//把树边存起来 
            add_edge(E[i].u, E[i].v, E[i].w), add_edge(E[i].v, E[i].u, E[i].w);
            id[Ecnt] = id[Ecnt - 1] = E[i].id;//记录每条边的标号 
        }	
    }
    void find_max(int x, int f) { 
       for (int i = head[x]; i; i = e[i].nxt) {
        	int v = e[i].v;
        	if (v == f || del[i] || v == 1) continue;
        	   if (e[i].w >= e[mx[x]].w) mx[v] = i;//存的是最大边在图中的编号 
        	   else mx[v] = mx[x];
        	   find_max(v,  x);
    	}
    }
    main(void){
    	freopen("a.in", "r", stdin);
        n = read(), m = read(), k = read();
        for (int i = 1; i <= m; i++)
        E[i].u = read(), E[i].v = read(), E[i].w = read(), E[i].id = i;
    	forest();
        for (int i = 1; i <= m; i++) {
          if (E[i].u != 1 && E[i].v != 1) continue;
          int u = find(E[i].u), v = find(E[i].v);
          if (u == v) vec.push_back(i);//将 1 号点连出的非树边存起来 
          else fa[u] = v, add_edge(E[i].u, E[i].v, E[i].w), T.push_back(E[i].id), k--;
    	}
    	
    	for (int i = 2; i <= n; i++) if (find(i) != find(i - 1)){printf("-1");return 0;}	    
    	
    	if (k < 0){printf("-1");return 0;} // 1 号点连出的边一定会大于 k 
    	
    	for (int i = 1; i <= k; i++) {
    	 	if (!vec.size()){ printf("-1");return 0;} 
    		memset(mx, 0, sizeof mx);
    		for (int j = head[1]; j; j = e[i].nxt) {
    		    int v = e[i].v;
    			find_max(v, 1);
    		} 
    		int now = -1, ans = 0x3f3f3f3f3f3f;
    	    for (int j = vec.size() - 1; j >= 0; j--)  {
    	       int p = vec[j], var = max(E[p].u, E[p].v);//枚举的一条非树边进行交换 
    	       if (E[p].w - e[mx[var]].w < ans) ans = E[p].w - e[mx[var]].w, now = j; 
    		}
    		int p = vec[now], var = E[p].u == 1 ? E[p].v:E[p].u;
    		add_edge(E[p].u, E[p].v, E[p].w), add_edge(E[p].v, E[p].u, E[p].w);
    		T.push_back(E[p].id);
    		fag[id[mx[var]]] = 1, del[mx[var]] = del[mx[var] ^ 1] = 1;
    		vec.erase(vec.begin() + now);
    	} 
    	printf("%d
    ", n - 1);
        for (int i = T.size() - 1; i >= 0; i--) if(!fag[T[i]]) printf("%d ", T[i]); 
        return 0;
    }
    

    还有一种方法:

    另一种方法:考虑将 1 周围所有边权值 +delta,使得权值修正后的图存在点 1 度数为 k 的最小生成树 Delta 可以二分

  • 相关阅读:
    饮冰三年-人工智能-Python-35权限管理(万能的权限通用模块)
    饮冰三年-人工智能-Python-34CRM项目实战
    饮冰三年-人工智能-Python-33权限管理(通过配置快速生成列表)
    集腋成裘-15-MongoDB系列-02Find详细
    集腋成裘-14-MongoDB系列-01初识MongoDB
    饮冰三年-人工智能-Python-33博客园山寨版之报障管理
    饮冰三年-人工智能-Python-32博客园山寨版之后台管理
    JVM内存管理
    连接池实现原理
    定时器
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15054418.html
Copyright © 2011-2022 走看看