BZOJ 1196 [HNOI2006]公路修建问题:二分 + 贪心生成树check(类似kruskal)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1196

题意:

  n个城市,m对城市之间可以修公路。

  公路有两种,一级公路和二级公路,在第i对城市之间修的花费分别为c1[i],c2[i]。

  你需要修n-1条公路,连接起所有城市(一棵树),并且至少有k条一级公路。

  问你“花费最大的一条公路”的最小花费(最大值最小)。

 

题解:

  O(log(c))二分 * O(m)生成树check + O(mlog(m))预先将边排序

 

  二分:

    二分答案(可能的最大一条公路的花费)。

  

  check:

    现在二分的值为now,要求所有边的花费 <= now.

    所以贪心下:

      对于每条边,只要不超过now,并且属于不同集合:能修一级就修一级,不行再修二级,然后加入生成树中。

      并且还要先考虑最有可能可以修一级公路的边,所以预先将所有边按c1从小到大排序。

      整个过程类似kruskal。。。

    最后如果是一棵树(cnt==n-1),并且一级公路数量足够(super>=k),则答案可行。

 

AC Code:

  1 #include <iostream>
  2 #include <stdio.h>
  3 #include <string.h>
  4 #include <algorithm>
  5 #include <vector>
  6 #define MAX_N 10005
  7 #define COST_MAX 30005
  8 
  9 using namespace std;
 10 
 11 struct Edge
 12 {
 13     int sour;
 14     int dest;
 15     int fst;
 16     int sec;
 17     Edge(int _sour,int _dest,int _fst,int _sec)
 18     {
 19         sour=_sour;
 20         dest=_dest;
 21         fst=_fst;
 22         sec=_sec;
 23     }
 24     Edge(){}
 25     friend bool operator < (const Edge &a,const Edge &b)
 26     {
 27         return a.fst<b.fst;
 28     }
 29 };
 30 
 31 int n,m,k;
 32 int ans;
 33 int par[MAX_N];
 34 vector<Edge> edge;
 35 
 36 void read()
 37 {
 38     cin>>n>>k>>m;
 39     m--;
 40     int a,b,c1,c2;
 41     for(int i=0;i<m;i++)
 42     {
 43         cin>>a>>b>>c1>>c2;
 44         edge.push_back(Edge(a,b,c1,c2));
 45     }
 46 }
 47 
 48 void init_union_find()
 49 {
 50     for(int i=1;i<=n;i++)
 51     {
 52         par[i]=i;
 53     }
 54 }
 55 
 56 int find(int x)
 57 {
 58     return par[x]==x?x:par[x]=find(par[x]);
 59 }
 60 
 61 void unite(int x,int y)
 62 {
 63     int px=find(x);
 64     int py=find(y);
 65     if(px==py) return;
 66     par[px]=py;
 67 }
 68 
 69 bool same(int x,int y)
 70 {
 71     return find(x)==find(y);
 72 }
 73 
 74 bool check(int now)
 75 {
 76     init_union_find();
 77     int cnt=0;
 78     int super=0;
 79     for(int i=0;i<m;i++)
 80     {
 81         Edge temp=edge[i];
 82         if(min(temp.fst,temp.sec)<=now)
 83         {
 84             if(!same(temp.sour,temp.dest))
 85             {
 86                 cnt++;
 87                 if(temp.fst<=now) super++;
 88                 unite(temp.sour,temp.dest);
 89             }
 90         }
 91     }
 92     return cnt==n-1 && super>=k;
 93 }
 94 
 95 void solve()
 96 {
 97     sort(edge.begin(),edge.end());
 98     int lef=0;
 99     int rig=COST_MAX;
100     while(rig-lef>1)
101     {
102         int mid=(lef+rig)/2;
103         if(check(mid)) rig=mid;
104         else lef=mid;
105     }
106     ans=rig;
107 }
108 
109 void print()
110 {
111     cout<<ans<<endl;
112 }
113 
114 int main()
115 {
116     read();
117     solve();
118     print();
119 }

 

posted @ 2017-09-08 02:06  Leohh  阅读(171)  评论(0编辑  收藏  举报