EOJ 3247:铁路修复计划


铁路修复计划

Time limit per test: 2.0 seconds

Time limit all tests: 15.0 seconds

Memory limit: 256 megabytes

在 A 国有很多城际铁路。这些铁路都连接两个城市(城市从  1  到  n  编号),可以双向通行,使得任意两个城市之间都由铁路网联系起来。

不过在一次星球大战之后,所有的铁路都经历了不同程度的损伤以至于无法通行了。由于经费紧缺,A 国政府不愿意再出资造新的铁路。对于原有的城际铁路,根据铁路的实际情况,有以下两种处理办法:

  1. 使用国内技术进行修复:主要针对损坏情况不是很严重的铁路。国内公司会对铁路状况进行评估,然后如实开出铁路修复的费用。
  2. 使用国外技术进行修复:主要针对损坏情况严重的铁路。国外公司也会对铁路情况进行评估,然后按照铁路实际修复费用的  k  倍来收费(其中  k  是一个由国外公司决定的实数,不管怎么说,优惠是不可能的,所以  k1 )。

A国政府修复铁路的总预算是  M ,目标是要让任意两个城市之间都能通过铁路联系起来。在预算不够且能够完成目标的条件下,显然没必要修复每一条铁路。

国外公司通过不知什么途径了解到了 A 国政府的总预算  M ,他们现在要把  k  定下来,并且希望  k  尽可能得大。但  k 又不能太大,不然,如果 A 国政府发现无法完成任务的话,整个订单都会泡汤。

Input

测试数据包含不超过 30 个测试文件。每个测试文件是单个测试点。

第一行是三个整数  n,m,M   (2n105,n1mmin{105,n(n1)2},1M1015)

接下来  m  行,每行四个整数  ui,vi,ti,fi 。表示一条城际铁路,连接着  ui  和  vi  城市, ti  表示铁路实际修复费用。 fi=1  表示只能由国外公司修复, fi=0  表示由国内公司修复。 (1ui,vin,uivi,1ti106,fi{0,1}) 。输入保证两个城市之间不会存在多条铁路。

输入保证:

  • 在国外公司不乱收费  (k=1)  的情况下,使用预算能完成要求。
  • 完全不使用国外技术,只使用国内技术,是不能完成目标的。

Output

求  k  的最大值。输出答案与标准答案相差不超过  106  即判为正确。

Examples

input
3 3 9
1 2 1 1
1 3 2 0
2 3 1 1
output
7.000000
input
3 3 9
1 2 1 1
1 3 2 1
2 3 2 1
output
3.000000
下面是官方题解:

G. 铁路修复计划

二分  k ,然后每次对边重新赋值排序后,就是经典的 MST 问题了。
复杂度  O(nlog2n)

思路详述:

题目中k最小是1,那么现在找一个大一点的数high,k一定是1~high中的某个数。(因为k就是个实数,一定在1~正无穷内,

因此找个大点的数给k一个合适的界限就好)开始二分。找到mid.以当前mid为k值,求出各个边的权,并将其按从小到大

排序。用Kruskal求MST,如果最后求得的MST>M。说明当前mid大了,不能满足要求,则在左区间的进行二分查找,否则,

在右区间进行查找。

初始自己对人家给的思路理解得并不透彻。我以为结束条件还是low<high,发现这样答案和题中给出的输出差距很大。后来

明白了,这个是实数类型的二分查找。我应该尽可能的不停的去二分,才能非常接近某个确定的值。所以二分查找的次数要

尽可能多些。但也要注意别太大了,太大了会导致超时,而且这个次数的上限特别好试探,很容易就可以找出。边权排序运

算符重载要写在结构体内,原来是在外面写了构造函数,并且high是1e10二分次数进行了55次,提交超时两次。后来把运算

符重载写在结构体内,我把high扩大1e15,并且二分次数扩大到70,提交过了,说明在外面写一个cmp排序应该没直接在结构

体写运算符重载快。


#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>

using namespace std;

const int maxn = 100003;
const double eps = 1e-7;
int father[maxn];
double ans;
int n,m;
double M;
struct Edge
{
    int u;  ///端点
    int v;  ///端点
    double w;  ///权值
    int f;     ///国内修还是国外修
    bool operator < (const Edge &e1) const
    {
        return w < e1.w;
    }
}edge[maxn<<4],tmp[maxn<<4]; 
///tmp临时存放边的数组
void make_set()
{
    for(int i = 1; i <= n; i++)
        father[i] = i;
}
int find_set(int x)
{
    int root = x;
    while(root != father[root]) root = father[root];
    while(x != father[x]) {int temp = father[x]; father[x] = root; x = temp;}
    return root;
}
void union_set(int x,int y)
{
    father[x] = y;
}
int Kruskal(double k) ///假设k值,求MST
{
    make_set();       ///不要忘记初始化并查集
    for(int i = 0; i < m; i++)
    {
        tmp[i] = edge[i];
        if(tmp[i].f) 
            tmp[i].w = k*edge[i].w;
    }
    sort(tmp,tmp+m); ///对这些边进行从小到大排序。
    double mst = 0;
    int num = 0;
    for(int i = 0; i < m; i++)
    {
        int u = tmp[i].u;
        int v = tmp[i].v;
        int fu = find_set(u);
        int fv = find_set(v);
        if(fu != fv)
        {
            union_set(fu,fv);  ///合并集合
            num++;             ///边数加1
            mst = mst + tmp[i].w;
        }
        if(num == n-1)
            break;
    }
    ///最小生成树值比M大。
    if(mst - M > eps) return -1;
    else return 1;
}
int main()
{
    while(~scanf("%d%d%lf",&n,&m,&M))  ///输入城市个数,道路条数,最大钱数
    {
        for(int i = 0; i < m; i++)
        {
            scanf("%d%d%lf%d",&edge[i].u,&edge[i].v,&edge[i].w,&edge[i].f);
        }
        double low,high,mid;
        low = 1;
        high = 1e15;  ///定义的high大一些
        /**这个循环次数越多,二分次数越多,答案越精确。发现循环次数少的时候
        输出的数和答案的差距很大,题目要求答案差距不能大于1e-6.所以尽量循环
        次数多点**/
        for(int i = 1; i <= 70; i++) 
        {
            mid = (low+high)/2;
            int ans = Kruskal(mid);
            if(ans == -1)
                high = mid-1;
            else
                low = mid+1;
        }
        printf("%lf\n",(low+high)/2);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值