P2121 拆地毯
解题思路
问题分析
- 
题目描述:从m条地毯中选择最多k条,要求选出的地毯不形成环,且美丽度之和最大 
- 
关键限制: - 
选出的地毯不能形成环(即形成森林) 
- 
最多选k条地毯 
- 
需要最大化美丽度总和 
 
- 
算法选择
- 
最大生成森林:这是最小生成树(MST)问题的变种 
- 
Kruskal算法变形: - 
按美丽度降序排序 
- 
贪心地选择美丽度大的边 
- 
使用并查集避免环的形成 
- 
在选够k条边或形成生成树时停止 
 
- 
实现步骤
- 
预处理: - 
读取所有地毯信息 
- 
按美丽度从大到小排序 
 
- 
- 
核心算法: - 
初始化并查集 
- 
遍历排序后的边,选择不形成环的边 
- 
累计美丽度,直到选够k条边或无法继续选择 
 
- 
- 
终止条件: - 
已选k条边 
- 
或已形成生成树(选n-1条边) 
 
- 
正确性证明
- 
贪心选择:每次选择当前最大美丽度的有效边,确保局部最优导致全局最优 
- 
无环保证:通过并查集确保不会连接已连通的顶点 
- 
结果最优:由于从大到小选择,最终的和必然是最大的可能值 
复杂度分析
- 
排序:O(m log m)(主要瓶颈) 
- 
Kruskal:O(m α(n))(α为反阿克曼函数) 
- 
总体:O(m log m),对于m≤1e5完全可行 
关键点说明
- 
排序方向:按美丽度降序排序(与标准Kruskal相反) 
- 
停止条件:两个条件满足其一即可停止 
- 
并查集作用:高效管理连通分量,避免环的形成 
#include<bits/stdc++.h> using namespace std; const int N = 2e5 + 10; // 定义边的结构体:x和y是顶点,z是美丽度(权重) struct node { int x, y, z; }; node a[N]; // 存储所有地毯(边)信息 int n, m, k; // n:区域数, m:地毯数, k:最多保留地毯数 int f[N]; // 并查集数组 // 并查集查找函数(带路径压缩) int find(int x) { if(f[x] != x) f[x] = find(f[x]); return f[x]; } // 并查集合并函数 void merge(int x, int y) { int fx = find(x), fy = find(y); f[fy] = fx; } // 边按美丽度从大到小排序的比较函数 bool cmp(node a, node b) { return a.z > b.z; } // Kruskal算法变形实现 void kruskal() { // 初始化并查集 for(int i = 1; i <= n; i++) f[i] = i; int sum = 0, ans = 0; // sum:已选边数, ans:美丽度总和 // 遍历所有边(已按美丽度降序排序) for(int i = 1; i <= m; i++) { int x = a[i].x, y = a[i].y; // 如果两点不在同一连通块 if(find(x) != find(y)) { merge(x, y); // 合并连通块 sum++; // 已选边数+1 ans += a[i].z; // 累加美丽度 // 达到k条边或已形成生成树时停止 if(sum == k || sum == n - 1) break; } } cout << ans; // 输出最大美丽度和 } int main() { cin >> n >> m >> k; // 读取所有地毯信息 for(int i = 1; i <= m; i++) { cin >> a[i].x >> a[i].y >> a[i].z; } // 按美丽度降序排序 sort(a + 1, a + 1 + m, cmp); // 执行算法 kruskal(); return 0; }
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号