1. 程式人生 > >山山賺錢記 二分+圖論

山山賺錢記 二分+圖論

char 滿足 100% 事情 sum include 報酬 struct 什麽

【題目描述】
又有老師讓山山做事情了,不過這次的任務雖然有時體力活,但是山山做的心甘情願,
為什麽呢?因為有 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;
}

山山賺錢記 二分+圖論