【BZOJ1196】公路修建問題
阿新 • • 發佈:2018-11-05
題目連結:https://www.lydsy.com/JudgeOnline/problem.php?id=1196
看起來和免費道路那道題是有共同點的,但細細一想,其實二者並不一樣。免費道路要求恰好有k條鵝卵石路,而本題要求至少k條一級道路。
本題是想讓邊權最大的邊最小(別去想最小生成樹的性質!!!),所以可以二分答案,因為必然最大邊權越大越容易滿足,任意一條邊都可以成為一級道路。
我們畢竟是要驗證答案,所以應該先加一遍一級道路,然後再去補二級道路,這樣才能夠保證符合條件的不被判斷為不符合條件。
1 #include <cstdio> 2AC程式碼(BZOJ、二分)#include <algorithm> 3 4 using namespace std; 5 6 inline int get_num() { 7 int num = 0; 8 char c = getchar(); 9 while (c < '0' || c > '9') c = getchar(); 10 while (c >= '0' && c <= '9') 11 num = num * 10 + c - '0', c = getchar(); 12 returnnum; 13 } 14 15 const int maxn = 1e4 + 5, maxm = 2e4 + 5; 16 17 struct Edge { 18 int u, v, w1, w2; 19 } edge[maxm]; 20 21 int n, k, m, maxw, fa[maxn]; 22 23 int dj_find(int i) { 24 if (fa[i] == i) return i; 25 else return fa[i] = dj_find(fa[i]); 26 } 27 28 inline voiddj_merge(int a, int b) { 29 fa[dj_find(a)] = dj_find(b); 30 } 31 32 inline int check(int x) { 33 int cnt = 0; 34 for (int i = 1; i <= n; ++i) fa[i] = i; 35 for (int i = 1; i <= m - 1; ++i) { 36 if (edge[i].w1 > x) continue; 37 int u = edge[i].u, v = edge[i].v; 38 if (dj_find(u) != dj_find(v)) { 39 dj_merge(u, v); 40 ++cnt; 41 } 42 } 43 if (cnt < k) return 0; 44 for (int i = 1; i <= m - 1; ++i) { 45 if (edge[i].w2 > x) continue; 46 int u = edge[i].u, v = edge[i].v; 47 if (dj_find(u) != dj_find(v)) { 48 dj_merge(u, v); 49 ++cnt; 50 } 51 } 52 if (cnt != n - 1) return 0; 53 else return 1; 54 } 55 56 int main() { 57 n = get_num(), k = get_num(), m = get_num(); 58 for (int i = 1; i <= m - 1; ++i) { 59 edge[i].u = get_num(), edge[i].v = get_num(); 60 edge[i].w1 = get_num(), edge[i].w2 = get_num(); 61 maxw = max(maxw, edge[i].w1); 62 } 63 int l = 1, r = maxw; 64 while (l < r) { 65 int mid = l + (r - l) / 2; 66 if (check(mid)) r = mid; 67 else l = mid + 1; 68 } 69 printf("%d", l); 70 return 0; 71 }
然而,做完一道題,重新整理世界觀。。。
本著騙一下AC量的原則,我把相同的程式碼交到了洛谷上,,,結果,呵呵呵。。。
一看,哦輸出格式變了。我就稍微一改,死活過不去,用Special Judge評,老是說我圖沒連通。那我快去看看題解吧。
居然,還可以用貪心做!那我改去學學貪心好了。。。
參照Kruskal演算法的做法,我們先按c1從小到大排一下序,然後從裡面條k條,肯定剛好k條最好;然後選擇二級道路肯定會更好些,所以我們再按照c2排序,挑完剩下的。
注意,並不保證最小的c1大於最大的c2;若c1相同則按c2從大到小排序,因為這樣可以保證後面最優。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 inline int get_num() { 7 int num = 0; 8 char c = getchar(); 9 while (c < '0' || c > '9') c = getchar(); 10 while (c >= '0' && c <= '9') 11 num = num * 10 + c - '0', c = getchar(); 12 return num; 13 } 14 15 const int maxn = 1e4 + 5, maxm = 2e4 + 5; 16 17 struct Edge { 18 int id, u, v, w1, w2; 19 } edge[maxm]; 20 21 bool comp1(const Edge& lhs, const Edge& rhs) { 22 if (lhs.w1 == rhs.w1) return lhs.w2 > rhs.w2; 23 else return lhs.w1 < rhs.w1; 24 } //w1相等,則留w2小的,之後會更優 25 26 bool comp2(const Edge& lhs, const Edge& rhs) { 27 return lhs.w2 < rhs.w2; 28 } 29 30 int fa[maxn]; 31 32 int dj_find(int i) { 33 if (fa[i] == i) return i; 34 else return fa[i] = dj_find(fa[i]); 35 } 36 37 inline void dj_merge(int a, int b) { 38 fa[dj_find(a)] = dj_find(b); 39 } 40 41 struct Ans { 42 int id, type; 43 bool operator < (const Ans& rhs) const { 44 return id < rhs.id; 45 } 46 } ans[maxn]; 47 48 int main() { 49 int n = get_num(), k = get_num(), m = get_num(); 50 int cnt = 0, me = 0, aeid = 0; 51 for (int i = 1; i <= m - 1; ++i) { 52 edge[i].id = i; 53 edge[i].u = get_num(), edge[i].v = get_num(); 54 edge[i].w1 = get_num(), edge[i].w2 = get_num(); 55 } 56 sort(edge + 1, edge + m, comp1); 57 for (int i = 1; i <= n; ++i) fa[i] = i; 58 for (int i = 1; i <= m - 1; ++i) { 59 int u = edge[i].u, v = edge[i].v; 60 if (dj_find(u) != dj_find(v)) { 61 dj_merge(u, v); 62 me = max(me, edge[i].w1); 63 ans[++aeid].id = edge[i].id, ans[aeid].type = 1; 64 if (++cnt == k) break; 65 } 66 } 67 sort(edge + 1, edge + m, comp2); 68 for (int i = 1; i <= m - 1; ++i) { 69 int u = edge[i].u, v = edge[i].v; 70 if (dj_find(u) != dj_find(v)) { 71 dj_merge(u, v); 72 me = max(me, edge[i].w2); 73 ans[++aeid].id = edge[i].id, ans[aeid].type = 2; 74 } 75 } 76 printf("%d\n", me); 77 sort(ans + 1, ans + n); 78 for (int i = 1; i <= n - 1; ++i) 79 printf("%d %d\n", ans[i].id, ans[i].type); 80 return 0; 81 }AC程式碼(洛谷、貪心)
當然,並不是說二分不能做,二分也可以,是我寫錯了而已。
我以為跑完二分,順便記錄每一次的答案即可,但顯然不對,因為你不知道最後一次是對的還是錯的,所以我們需要在求出答案之後,再建一次樹。
1 #include <cstdio> 2 #include <algorithm> 3 4 using namespace std; 5 6 inline int get_num() { 7 int num = 0; 8 char c = getchar(); 9 while (c < '0' || c > '9') c = getchar(); 10 while (c >= '0' && c <= '9') 11 num = num * 10 + c - '0', c = getchar(); 12 return num; 13 } 14 15 const int maxn = 1e4 + 5, maxm = 2e4 + 5; 16 17 struct Edge { 18 int u, v, w1, w2; 19 } edge[maxm]; 20 21 int n, k, m, maxw, fa[maxn]; 22 23 int dj_find(int i) { 24 if (fa[i] == i) return i; 25 else return fa[i] = dj_find(fa[i]); 26 } 27 28 inline void dj_merge(int a, int b) { 29 fa[dj_find(a)] = dj_find(b); 30 } 31 32 struct Ans { 33 int id, type; 34 bool operator < (const Ans& rhs) const { 35 return id < rhs.id; 36 } 37 } ans[maxn]; 38 39 inline int check(int x) { 40 int cnt = 0; 41 for (int i = 1; i <= n; ++i) fa[i] = i; 42 for (int i = 1; i <= m - 1; ++i) { 43 if (edge[i].w1 > x) continue; 44 int u = edge[i].u, v = edge[i].v; 45 if (dj_find(u) != dj_find(v)) { 46 dj_merge(u, v); 47 ++cnt; 48 } 49 } 50 if (cnt < k) return 0; 51 for (int i = 1; i <= m - 1; ++i) { 52 if (edge[i].w2 > x) continue; 53 int u = edge[i].u, v = edge[i].v; 54 if (dj_find(u) != dj_find(v)) { 55 dj_merge(u, v); 56 ++cnt; 57 } 58 } 59 if (cnt != n - 1) return 0; 60 else return 1; 61 } 62 63 inline void build(int x) { 64 int cnt = 0; 65 for (int i = 1; i <= n; ++i) fa[i] = i; 66 for (int i = 1; i <= m - 1; ++i) { 67 if (edge[i].w1 > x) continue; 68 int u = edge[i].u, v = edge[i].v; 69 if (dj_find(u) != dj_find(v)) { 70 dj_merge(u, v); 71 ans[++cnt].id = i, ans[cnt].type = 1; 72 } 73 } 74 for (int i = 1; i <= m - 1; ++i) { 75 if (edge[i].w2 > x) continue; 76 int u = edge[i].u, v = edge[i].v; 77 if (dj_find(u) != dj_find(v)) { 78 dj_merge(u, v); 79 ans[++cnt].id = i, ans[cnt].type = 2; 80 } 81 } 82 } 83 84 int main() { 85 n = get_num(), k = get_num(), m = get_num(); 86 for (int i = 1; i <= m - 1; ++i) { 87 edge[i].u = get_num(), edge[i].v = get_num(); 88 edge[i].w1 = get_num(), edge[i].w2 = get_num(); 89 maxw = max(maxw, edge[i].w1); 90 } 91 int l = 1, r = maxw; 92 while (l < r) { 93 int mid = l + (r - l) / 2; 94 if (check(mid)) r = mid; 95 else l = mid + 1; 96 } 97 printf("%d\n", l); 98 build(l); 99 sort(ans + 1, ans + n); 100 for (int i = 1; i <= n - 1; ++i) 101 printf("%d %d\n", ans[i].id, ans[i].type); 102 return 0; 103 }AC程式碼(洛谷、二分)