秦始皇修路
题意:给出n个城市与m条路,求一种修建n-1条路的方案,使得A/B最大。A指可由你选择的一条不用付钱的路i,它连接的两个城市的总人口。B指路i的长度。
算法:最小瓶颈路
思路:求出最小生成树。枚举任意两点作为道路(u,v)的两端,若答案边(u,v)不在最小生成树中,连该边即成环,于是删除u,v在MST上的路径中的最长边(最小瓶颈路:两个结点之间一条最长边最短的路径),否则直接将边从最小生成树中删除。
若数据范围更大,可以用倍增的方法处理任意两点间的最小瓶颈路。
#include
#include
#include
#include
#include
const int sm = 1e3+10;
const int sn = 1e6+10;
int T,N,S,Ct,l;
int hd[sm],to[sm<<1],nxt[sm<<1];
int Fa[sm],X[sm],Y[sm],C[sm];
double mxc[sm][sm];
double Ans,tot,w[sm<<1];
struct Edge {
int Fm,To;double L;
bool ex;
}Ed[sn];
std::vector<int>g;
bool cmp(Edge x,Edge y) { return x.Ltemplate <typename T> T Max(T x,T y) { return x>y?x:y; }
void Ins(int u,int v,double len) { Ed[++S]=(Edge) {u,v,len};}
double Len(int x,int y,int a,int b) { return (double)sqrt((x-a)*(x-a)+(y-b)*(y-b)); }
void read(int &x) {
x=0;char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}
void init() {
S=Ct=0,Ans=tot=0;
l=Max(N,N*(N-1)/2);
for(int i=1;i<=l;++i) {
if(i<=N)hd[i]=0;
Ed[i].ex=0;
}
if(!g.empty()) g.clear();
memset(mxc,0,sizeof(mxc));
}
void Add(int u,int v,double len) {
to[++Ct]=v,nxt[Ct]=hd[u],hd[u]=Ct,w[Ct]=len;
to[++Ct]=u,nxt[Ct]=hd[v],hd[v]=Ct,w[Ct]=len;
}
int Find(int x) {
if(x!=Fa[x]) Fa[x]=Find(Fa[x]);
return Fa[x];
}
void Kruskal() {
int x,y;
for(int i=1;i<=N;++i) Fa[i]=i;
std::sort(Ed+1,Ed+S+1,cmp);
for(int i=1;i<=S;++i) {
x=Find(Ed[i].Fm);
y=Find(Ed[i].To);
if(x!=y) {
Add(Ed[i].Fm,Ed[i].To,Ed[i].L);
Ed[i].ex=1;
Fa[x]=y,tot+=Ed[i].L;
if((Ct>>1)==N-1) break;
}
}
}
void Dfs(int x,int fa,double LEN) {
for(int i=0;ifor(int i=hd[x];i;i=nxt[i])
if(to[i]!=fa) Dfs(to[i],x,w[i]);
}
void Calc() {
for(int i=1,x,y,z;i<=S;++i) {
x=Ed[i].Fm,y=Ed[i].To,z=C[x]+C[y];
if(Ed[i].ex) Ans=Max(Ans,z*1.0/(tot-Ed[i].L));
else Ans=Max(Ans,z*1.0/(tot-mxc[x][y]));
}
}
int main() {
read(T);
while(T--) {
init(),read(N);
for(int i=1;i<=N;++i)
read(X[i]),read(Y[i]),read(C[i]);
for(int i=1;ifor(int j=i+1;j<=N;++j)
Ins(i,j,Len(X[i],Y[i],X[j],Y[j]));
Kruskal(),Dfs(1,0,0),Calc();
printf("%.2lf\n",Ans);
}
return 0;
}