山山赚钱记 二分+图论

【题目描述】
又有老师让山山做事情了,不过这次的任务虽然有时体力活,但是山山做的心甘情愿,
为什么呢?因为有 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\n",ans);
	
	return 0;
}

 

posted @ 2017-10-28 08:05  GSHDYJZ  阅读(282)  评论(0编辑  收藏  举报