All-Ukrainian School Olympiad in Informatics A. Gift
原题链接
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 ; }