1. 程式人生 > >【BZOJ1196】公路修建問題

【BZOJ1196】公路修建問題

題目連結:https://www.lydsy.com/JudgeOnline/problem.php?id=1196


 

看起來和免費道路那道題是有共同點的,但細細一想,其實二者並不一樣。免費道路要求恰好有k條鵝卵石路,而本題要求至少k條一級道路。

本題是想讓邊權最大的邊最小(別去想最小生成樹的性質!!!),所以可以二分答案,因為必然最大邊權越大越容易滿足,任意一條邊都可以成為一級道路。

我們畢竟是要驗證答案,所以應該先加一遍一級道路,然後再去補二級道路,這樣才能夠保證符合條件的不被判斷為不符合條件。

 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 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程式碼(BZOJ、二分)

 

然而,做完一道題,重新整理世界觀。。。

本著騙一下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程式碼(洛谷、二分)