官方题解 | 可恶の施工
2025-04-12 18:31:33
发布于:云南
94阅读
0回复
0点赞
T5 可恶の施工:最小生成树、贪心
题意简化
给定一个无向带权连通图,求在保证图连通的前提下,最少删除多少条边使得剩余边的总权值不超过给定的限制 。若无法满足条件,输出 -1。
关键观察
- 最优解的结构一定是 保留权值较小的边,同时保证图的连通性。
 - 这与 最小生成树 (MST) 的性质一致:MST 是权值和最小的连通子图。
 - 因此,问题转化为:在 MST 的基础上,尽可能多地保留权值小的额外边,使得总权值不超过 。
 
算法思路
- 
构建最小生成树:
- 使用 Kruskal 算法求出 MST,记录其权值和 和使用的边数 。
 - 若 ,直接输出 
-1(无解)。 
 - 
贪心添加额外边:
- 将 非 MST 边 按权值从小到大排序。
 - 依次尝试添加这些边,直到总权值超过 。
 - 最终删除的边数为 总边数 - 保留的边数。
 
 
时间复杂度分析
- Kruskal 算法:排序边 ,并查集操作 ,总体 。
 - 贪心添加额外边:遍历至多 条边,。
 - 总复杂度:,其中 为测试用例数量。
 
部分分策略
| 测试点 | 数据范围 | 特殊性质 | 可用算法 | 预计得分 | 
|---|---|---|---|---|
| 1-2 | 无 | 暴力枚举删边组合 | 10pts | |
| 3-4 | 无 | 枚举保留边数 + Kruskal | 30pts | |
| 5 | 所有边权相等 | 贪心保留最多边 | 25pts | |
| 6 | (树结构) | 树边权和是否超过 | 直接判断 | 10pts | 
| 7-10 | 无 | 正解算法 | 100pts | 
特殊性质说明:
- 性质 A:所有边权相等时,保留尽可能多的边即可,无需考虑权值大小。
 - 性质 B:图为树时,若 则无解,否则必须保留全部树边(不能删除任何边,否则不连通)。
 
正解代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long
using namespace std;
struct node{
	int from, to, len;
	bool operator < (const node &b) const{
		return len < b.len;
	}
}edge[100005], edge2[100005];
int father[100005];
int find(int n){return (father[n] == n ? n : father[n] = find(father[n]));}
bool merge(int x, int y){
	int n = find(x), m = find(y);
	if(n == m) return 0;
	father[min(n, m)] = max(n, m);
	return 1;
}
int n, m, k, ct, ctt, cttt;
void solve(){
	cin >> n >> m >> k;
	ct = ctt = cttt = 0;
	for(int i = 1; i <= n; i++) father[i] = i;
	for(int i = 1; i <= m; i++){
		cin >> edge[i].from >> edge[i].to >> edge[i].len;
	}
	sort(edge + 1, edge + m + 1);
	for(int i = 1; i <= m; i++){
		if(merge(edge[i].from, edge[i].to)){
			ct++;
			ctt += edge[i].len;
			if(ct == n - 1){
				for(int j = i + 1; j <= m; j++) edge2[++cttt] = edge[j];
				break;
			}
		}else{
			edge2[++cttt] = edge[i];
		}
	}
	if(ctt > k || ct < n - 1){
		cout << "NO\n";
		return;
	}
	for(int i = 1; i <= cttt; i++){
		ctt += edge2[i].len, ct++;
		if(ctt > k){
			cout << m - ct + 1 << '\n';
			return;
		}
	}
	cout << "0\n";
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0), cout.tie(0);
	int t;
	cin >> t;
	while(t--) solve();
	return 0;
}
这里空空如也







有帮助,赞一个