• 山山赚钱记 二分+图论


    【题目描述】
    又有老师让山山做事情了,不过这次的任务虽然有时体力活,但是山山做的心甘情愿,
    为什么呢?因为有 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;
    }
    
  • 相关阅读:
    MSSQL2005和Access在SQL的某一种写法上的区别。update的一种写法不一致。
    博客园 记录 了解多一点
    马克斯4.0 采集规则的编写
    谷歌代码托管 GoogleCode中 关于 版本的一个写法
    晒晒名企大公司的工资收入
    Asp.net中DataBinder.Eval用法的总结
    Mastering Debugging in Visual Studio 2010 A Beginner's Guide
    Solution Configuration but not Platform in VS2010 Toolbar
    window.showdialog完全手册,解决模态窗口,传值和返回值问题
    从此不再惧怕URI编码:JavaScript及C# URI编码详解
  • 原文地址:https://www.cnblogs.com/gshdyjz/p/7745879.html
Copyright © 2020-2023  润新知