现在的位置: 首页 > 综合 > 正文

[Codeforces 125E] MST Company (单度限制最小生成树)

2018年01月12日 ⁄ 综合 ⁄ 共 1579字 ⁄ 字号 评论关闭

题意:

有N个点,M条边,要生成一颗树。使得边权最小,而且对于起点 1 有度限制(k)。如果可以,输出所选边,否则输出 -1.

这道题可以用度限制最小生成树来做,但是这道题和度限制最小生成树又有区别,这里要求根节点的度必须为K。而后者要求小于等于K的最小值。

所以这道题目可以采用二分来实现。

采用krusical来做最小生成树的时候,每次挑选最小可行边加入集合,所以可以预处理一下与根节点相连的边,变为 w[i] ' = mid + w[i]. 

通过二分mid,每次求最小生成树的时候记录与1结点相连的边数。

/*
ID: wuqi9395@126.com
PROG:
LANG: C++
*/
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<fstream>
#include<cstring>
#include<ctype.h>
#include<iostream>
#include<algorithm>
#define maxn 5500
#define maxm 100100
#define INF (1<<30)
#define PI acos(-1.0)
#define mem(a, b) memset(a, b, sizeof(a))
#define For(i, n) for (int i = 0; i < n; i++)
typedef long long ll;
using namespace std;
int n, m, k, x[maxm], y[maxm], w[maxm], p[maxm], f[maxn];
int cnt, ans[maxn], inx;
double l, r, mid;
bool inline cmp(int i, int j) {
    return (x[i] == 1) * mid + w[i] < (x[j] == 1) * mid + w[j];
}
int findroot(int x) {
    return f[x] = (f[x] == x ? f[x] : findroot(f[x]));
}
void work(bool flag) {
    cnt = inx = 0;
    for (int i = 1; i <= n; i++) f[i] = i;
    sort(p + 1, p + m + 1, cmp);
    for (int i = 1; i <= m; i++) {
        int j = p[i];
        int u = findroot(x[j]), v = findroot(y[j]);
        if (u != v && (cnt + (x[j] == 1) <= k || flag)) {
            f[u] = v;
            ans[inx++] = j;
            if (x[j] == 1) cnt++;
        }
    }
}
int main ()
{
    scanf("%d%d%d", &n, &m, &k);
    int tot = 0;
    for (int i = 1; i <= m; i++) {
        scanf("%d%d%d", x + i, y + i, w + i);
        p[i] = i;
        if (x[i] > y[i]) swap(x[i], y[i]);
        if (x[i] == 1) tot++;
    }
    //如果根节点的度数小于k,或者结点数大于1,而k == 0 一定不行
    if (tot < k || (n > 1 && k == 0)) {
        puts("-1");
        return 0;
    }
    //看能否生成一棵树
    mid = 0;
    work(1);
    if (inx + 1 < n) {
        puts("-1");
        return 0;
    }
    l = -1e5, r = 1e5;
    while(l + 1e-5 < r && cnt != k) {
        mid = (l + r) / 2;
        work(1);
        if (cnt < k) r = mid;
        else l = mid;
    }
    if (cnt != k) mid = (l + r) / 2;
    work(0);
    printf("%d\n", inx);
    for (int i = 0; i < inx - 1; i++) printf("%d ", ans[i]);
    if (inx) printf("%d\n", ans[inx - 1]);
}

抱歉!评论已关闭.