• 山山赚钱记 二分+图论


    【题目描述】
    又有老师让山山做事情了,不过这次的任务虽然有时体力活,但是山山做的心甘情愿,
    为什么呢?因为有 money 可以拿啦~啦啦啦,山山好开心啊好开心~~
    这次的任务是让山山 去铺水管,学校一共有 N 个中转点,有 M 条可供选择的水管道
    路,一条水管道路连接两个中转点,最后 山山铺完的水管必须保证任意两个中转点之间都
    可以互相送水(直接相连或间接相连都可以) ,并且所铺的水管数尽量少。铺每条水管道
    路有不同的报酬,也需要耗不同大小的体力,由于山山喜欢 money 但是不喜欢动,所以他
    希望他平均每单位的体力所赚到的钱最多。
    【输入格式】
    第一行两个整数 N,M,表示有 N 个中转点和 M 条可供选择的水管道路。
    下面 M 行,每行四个整数,第 i+1 行的 u,v,w,c,表示点 u 到点 v 间铺水
    管可得报酬 w 元,需要耗费 c 单位的体力
    【输出格式】
    一行一个实数,表示平均每单位体力最多赚多少钱。保留 4 位小数。
    【样例】
    money.in
    5 5
    1 2 20 5
    1 3 20 5
    1 4 20 5
    1 5 20 5
    2 3 23 1
    money.out
    5.1875
    【数据规模】
    100% N<=400, M<=10000


    这题用到了最小化平均值的思想和结论。

    首先我们二分,然后我们考虑怎么check,我们可以跑最大生成树,在保证边数的同时又可以check出是否符合条件。

    我们传过去一个mid值,然后我们知如果满足条件的话:sum(wi)/sum(ci)>=mid

    所以我们把式子转化成0>=sum(wi)-mid*sum(ci)

    于是我们把每条边的边权修改为wi-mid*ci,然后排序,跑克鲁斯卡尔,最后看看是否满足条件即可.

    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    
    #define ll long long
    #define il inline
    #define db double
    
    using namespace std;
    
    il int gi()
    {
    	int x=0,y=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    		{
    			if(ch=='-')
    				y=-1;
    			ch=getchar();
    		}
    	while(ch>='0'&&ch<='9')
    		{
    			x=x*10+ch-'0';
    			ch=getchar();
    		}
    	return x*y;
    }
    
    int n,m;
    
    db eps=0.000001;
    
    struct edge
    {
    	int from,to;
    	int w,c;
    	db nice;
    }e[100045];
    
    bool cmp(edge a,edge b)
    {
    	return a.nice>b.nice;
    }
    
    int fa[100045];
    
    int find(int x)
    {
    	if(fa[x]!=x)
    		fa[x]=find(fa[x]);
    	return fa[x];
    }
    
    il bool check(db mid)
    {
    	for(int i=1;i<=n;i++)
    		fa[i]=i;
    	for(int i=1;i<=m;i++)
    		e[i].nice=(db)e[i].w-((db)e[i].c*mid);
    
    	sort(e+1,e+1+m,cmp);
    
    	int sum=0;
    	db s=0;
    	for(int i=1;i<=m;i++)
    		{
    			int r1=find(e[i].from),r2=find(e[i].to);
    			if(r1!=r2)
    				{
    					fa[r2]=r1;
    					sum++;
    					s+=e[i].nice;
    				}
    			if(sum==n-1)
    				break;
    		}
    	
    	if(s>=0)
    		return 1;
    	else
    		return 0;
    }
    
    int main()
    {
    	freopen("money.in","r",stdin);
    	freopen("money.out","w",stdout);
    
    	n=gi(),m=gi();
    	
    	for(int i=1;i<=n;i++)
    		fa[i]=i;
    
    	int x,y,u,v;
    	for(int i=1;i<=m;i++)
    		{
    			x=gi(),y=gi(),u=gi(),v=gi();
    			e[i].from=x;
    			e[i].to=y;
    			e[i].w=u;
    			e[i].c=v;
    		}
    
    	db l=0,r=100000;
    	db ans;
    	while(r-l>=eps)
    		{
    			db mid=(l+r)/2;
    			if(check(mid))
    				{
    					ans=mid;
    					l=mid;
    				}
    			else
    				r=mid;
    		}
    	
    	printf("%.4f
    ",ans);
    	
    	return 0;
    }
    
  • 相关阅读:
    C#projectPropertyGrid 显示结构(展开逗号分隔)
    WPF 数据绑定
    WPF Binding值转换器ValueConverter使用简介(二)-IMultiValueConverter
    WPF Binding值转换器ValueConverter使用简介(一)
    Sql语句知识大全
    C#端口、IP正则
    C#基础知识整理
    C#UDP异步通信
    D365 添加导入按钮继承到系统导出
    D365 FO 创建DataEntity找不到natural key
  • 原文地址:https://www.cnblogs.com/gshdyjz/p/7745879.html
Copyright © 2020-2023  润新知