原题链接

tag: 最小生成树,贪心,*2200

题意

给定一个 个点, 条边的图,同时给定两个正整数 ,每条边有 两个权重,求图的一棵生成树 ,使得 ​ 最小,求出这个最小值。

题解

考虑暴力的做法,由于是双变量最优化问题,考虑枚举某一个变量对另外一个变量进行优化。在本题中可以枚举最大的 值,用所有小于 的边建图,然后在这张图上以最小化 最小生成树。这样做的时间复杂度为

考虑如何优化,可以发现以下性质:

  • 如果一个边在上一次枚举 的时候没有被选中,那么在后续的枚举(假设是从小到大枚举 值)中一定不会被选中。

因为前面已经可以选择一些拥有更小 的边来连接使相同的一些点连通,而且可选的边会越来越多,肯定没有必要选当前的边,直接删掉即可,这样可以保证每一轮所剩下的边只有最多 条。时间复杂度降为 ,大约

如果要继续优化,由于每次枚举 值时相对于上一轮只会多加入一条边,不用每次都重新排序然后跑 ,只需要在上一次得到的边集中按照插入排序的方式插入这条边,然后再跑 ,并将这一轮得到的边集传递给下一轮。

时间复杂度

代码

In queue中……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
#define PII pair<int, int>
#define pi acos(-1.0)
constexpr ll inf = 0x7fffffffffffffffll;

struct edge {
int u, v;
ll g, s;
bool operator==(const edge& a) const { return u == a.u && v == a.v && g == a.g && s == a.s; }
bool operator<(const edge& a) const { return s < a.s; }
};

struct DSU {
std::vector<int> p;
DSU(int n)
{
p.resize(n + 1);
std::iota(p.begin(), p.end(), 0);
}

int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}

bool same(int a, int b) { return find(a) == find(b); }

void merge(int a, int b) { p[find(a)] = find(b); }
};

int main()
{
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, m;
ll G, S;
cin >> n >> m;
cin >> G >> S;
vector<edge> e;
for (int i = 1; i <= m; i++) {
int u, v;
ll g, s;
cin >> u >> v >> g >> s;
if (u == v) continue;
e.push_back({u, v, g, s});
}
sort(e.begin(), e.end(), [](const edge& a, const edge& b) { return a.g < b.g; });
vector<edge> cur;
vector<edge> past;
ll ans = inf;
for (int i = 0; i < e.size(); i++) {
cur.assign(past.begin(), past.end());
cur.push_back(e[i]);
for (int j = cur.size() - 1; j >= 1; j--) {
if (cur[j].s < cur[j - 1].s) swap(cur[j], cur[j - 1]);
}
past.clear();
int cnt = 0;
DSU T(n);
ll max_g = 0, max_s = 0;
for (int j = 0; j < cur.size(); j++) {
auto [u, v, g, s] = cur[j];
if (!T.same(u, v)) {
T.merge(u, v);
past.push_back(cur[j]);
max_g = max(max_g, g), max_s = max(max_s, s);
cnt++;
if (cnt == n - 1) break;
}
}
if (cnt == n - 1) ans = min(ans, G * max_g + S * max_s);
}
ans = ans == inf ? -1 : ans;
cout << ans << endl;
return 0;
}