• 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 }
  • 相关阅读:
    java模式及其应用场景
    redis配置密码 redis常用命令
    Redis可视化工具Redis Desktop Manager使用
    String类和StringBuffer类的区别
    centos下搭建redis集群
    eclipse maven项目中使用tomcat插件部署项目
    什么是反向代理,如何区别反向与正向代理
    数据库连接池的原理
    归并排序
    asio-kcp源码分析
  • 原文地址:https://www.cnblogs.com/Leohh/p/7492718.html
Copyright © 2020-2023  润新知