【訓練題】最優比率生成樹 P1696
阿新 • • 發佈:2018-11-30
Description
FJ最近從政府獲得開發N塊廢棄牧區的許可,但這些牧區之間沒有道路,FJ打算先修建一些道路,便於他的奶牛們能自由地從一個牧區到達另一個牧區。
經過細緻考察,FJ告訴你牧區i與牧區j之間的距離d[i][j](公里)以及修這條道路的花費c[i][j]元。現在請你幫助FJ規劃應怎樣修建道路才能使每公里的花費最少。
Input
第1行:2個用空格隔開的整數:N和M。接下來的M行,每行四個正整數:x,y,d,c,分別表示牧區x和牧區y之間的距離為d,花費為c。
Output
輸出FJ修建道路單位公里的最小花費,保留2位小數。
Hint
N<=400,M<=10000
Solution
emmmmm……首先這道題求的是平均的單位公里的花費(就是從花費除以總公里數)而不是修每段路的單位公里數,所以要二分猜答案。然後需要明確的就是每次check的時候每條邊的邊權要處理成什麼。關係式:edge[i].w=(double)edge[i].c-(double)edge[i].d*mid;如果是符合條件的mid的話,這棵最小生成樹的邊權和應該等於0(當然,涉及到精度問題,可能判斷的時候用1e-6要好些吧。。。。雖然我用0還是過了嘻嘻)。
注意事項: 1.check的時候判斷的是邊權和而不是最大邊權,所以ans應該+=而不是=,因為mid表示的是一個平均值而不是最大值。 2.每次check之前ans要清零。 3.啊,我也不知道還能說什麼了。
#include<cstdio> #include<iostream> #include<cstring> #include<algorithm> #define maxn 20005 using namespace std; struct Edge{ int u; int v; int d; int c; double w; int next; friend bool operator < (Edge a,Edge b){ return a.w<b.w; } }edge[maxn]; int first[maxn],last[maxn],FA[maxn]; int node,x,y,z,c,n,m,cnt; double L,R,mid,ans; void addedge(int u,int v,int d,int c){ edge[++node]=(Edge){u,v,d,c,0,0}; if(first[u]==0)first[u]=node; else edge[last[u]].next=node; last[u]=node; } void init(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ scanf("%d%d%d%d",&x,&y,&z,&c); addedge(x,y,z,c); } } int dofind(int x){ if(FA[x]==x)return x; return FA[x]=dofind(FA[x]); } void dounion(int x,int y){ int dx=dofind(x),dy=dofind(y); if(dx!=dy){ FA[dx]=FA[dy]; } } bool dofinD(int x,int y){ return dofind(x)==dofind(y); } bool check(double mid){ cnt=0; ans=0; for(int i=1;i<=n;i++){ FA[i]=i; } for(int i=1;i<=m;i++){ edge[i].w=(double)edge[i].c-(double)edge[i].d*mid; } sort(edge+1,edge+m+1); for(int i=1;i<=m;i++){ int a=edge[i].u,b=edge[i].v; if(dofinD(a,b))continue; dounion(a,b); ans+=edge[i].w; cnt++; if(cnt==n-1)break; } if(cnt<n-1)return false; if(ans<=0)return true; return false; } int main(){ init(); L=0.0,R=10000.0; while(L<=R){ mid=(L+R)/2.0; if(!check(mid))L=mid+0.0000001; else R=mid-0.0000001; } printf("%.2lf",mid); return 0; }