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 可以二分

  • 相关阅读:
    June 26th 2017 Week 26th Monday
    June 25th 2017 Week 26th Sunday
    June 24th 2017 Week 25th Saturday
    June 23rd 2017 Week 25th Friday
    June 22nd 2017 Week 25th Thursday
    2018最佳网页设计:就是要你灵感爆棚!!!
    图片素材类Web原型制作分享-Pexels
    想要打动HR的心,UX设计师求职信究竟应该怎么写?
    【UXPA大赛企业专访】Mockplus:“设计替代开发”将成为现实
    2018年最好的医疗网站设计及配色赏析
  • 原文地址:https://www.cnblogs.com/Arielzz/p/15054418.html
Copyright © 2011-2022 走看看