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; }