山山賺錢記 二分+圖論
阿新 • • 發佈:2017-10-28
char 滿足 100% 事情 sum include 報酬 struct 什麽
下面 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
【題目描述】
又有老師讓山山做事情了,不過這次的任務雖然有時體力活,但是山山做的心甘情願,
為什麽呢?因為有 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; }
山山賺錢記 二分+圖論