K小生成树(kmst))(状态压缩的dp实现+并查集)

描述:

链接:https://ac.nowcoder.com/acm/problem/19855
来源:牛客网

给定一个n个点m条边的无向联通图G=(V,E),满足G中不存在自环。
你需要求G的一个生成树T,使得T的权值和在[L,R]内。
求满足条件的T的个数。
思路:压缩状态dp,把所有的边的选择的情况枚举,然后判断是否是生成树,是就加入数组,最后排序,然后查询二分
这题可以用dfs得到所有的生成树情况,具体就是遍历所有边,选择或者不选择它,不选择就进入下一条边的判断,
如果两个相邻边还不是联通的,则可以选择(满足生成树的性质,加入每条边时相邻的节点不连通),选择就把两个边
用并查集标记为同一连通分量,不要用路径压缩,因为等下还要回溯,回溯时就标记会原本的点,真的跪了....
AC代码:
#include<bits/stdc++.h>
using namespace std;
int fa[50];
int find(int x) { return fa[x] == x ? x : find(fa[x]); }
int a[50], b[50], c[50];
int n, m;
int book[100000];
int tot = 1;
void dfs(int step, int num, int sum) {
    if (m - step + num + 1 < n)return;
    if (step == m) {
        if (num == n - 1) {//进行了n-1次合并,所有点都在同一联通分量内!!!
            book[tot++] = sum;
        }
        return;
    }
    dfs(step + 1, num, sum);//不选
    int x = find(a[step]), y = find(b[step]);
    if (x != y) {//未连通
        fa[x] = y;
        dfs(step + 1, num + 1, sum + c[step]);
        fa[x] = x;
    }
}
int main() {
    for (int i = 1; i <= 20; i++) {
        fa[i] = i;
    }
    scanf("%d%d", &n, &m);
    for (int i = 0; i < m; i++) {
        scanf("%d%d%d", &a[i], &b[i], &c[i]);
    }
    dfs(0, 0, 0);//找出所有生成树的情况
    sort(book, book + tot);
    int q; scanf("%d", &q);
    while (q--) {
        int l, r; scanf("%d%d", &l, &r);
        int a = lower_bound(book, book + tot, l) - book;
        int b = upper_bound(book, book + tot, r) - book;
        printf("%d\n", b - a);
    }
    return 0;
}

 

posted @ 2021-03-17 23:48  cono奇犽哒  阅读(463)  评论(0)    收藏  举报