本题的题意是某一条路可以不用花钱,这条路所连接的两个城市权值之和为A,然后求出最小生成树所需要的代价B,求出最大的A/B
思路1
既然要求A/B的最大值, 就一定要A最大, B最小,
所以B需要求一下MST, A的话我们直接枚举每条边就好了,
如果该边已经在MST中, ans = max(ans, A/(B-w[i][j]));
否则ans = max(ans, A/(B-maxD[i][j]));
#include <stdio.h>
#include <algorithm>
#include <vector>
using namespace std;
struct node
{
int u,v,z;
double w;
bool vis;
bool operator<(const node&a){
return w<a.w;
}
}a[500010];
int fa[1010],x[1010],y[1010],z[1010];
double d[1010][1010];
vector<int>p[1010];
int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n,tot=0;
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
p[i].clear();
p[i].push_back(i);
fa[i]=i;
}
for(int i=1;i<=n;i++)scanf("%d%d%d",&x[i],&y[i],&z[i]);
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
double w=sqrt(1.0*(x[j]-x[i])*(x[j]-x[i])+1.0*(y[j]-y[i])*(y[j]-y[i]));
a[++tot].u=i,a[tot].v=j,a[tot].w=w;a[tot].vis=0;a[tot].z=z[i]+z[j];
//a[++tot].u=j,a[tot].v=i,a[tot].w=w;a[tot].vis=0;a[tot].z=z[i]+z[j];
}
sort(a+1,a+1+tot);
double ans=0;
for(int i=1,m=0;i<=tot;i++)
{
int u=find(a[i].u),v=find(a[i].v);
if(u==v)continue;
ans+=a[i].w;
fa[u]=v;
a[i].vis=1;
m++;
int l1=p[u].size(),l2=p[v].size();
for(int j=0;j<l1;j++)
for(int k=0;k<l2;k++)
d[p[u][j]][p[v][k]]=d[p[v][k]][p[u][j]]=a[i].w;
for(int j=0;j<l1;j++)p[v].push_back(p[u][j]);
if(m==n-1)break;
}
double res=0;
for(int i=1;i<=tot;i++)//枚举边
{
if(a[i].vis) //在最小生成树上
res=max(res,1.0*a[i].z/(ans-a[i].w));
else//不在
res=max(res,1.0*a[i].z/(ans-d[a[i].u][a[i].v]));//d[u][v]最长边
//printf("%d %d %lf %lf\n",a[i].vis,a[i].z,a[i].w,d[a[i].u][a[i].v]);
}
printf("%.2lf\n",res);
}
}
思路2
很清楚,因为有了这条路之后这两个点已经连通了,
其他的线段肯定是最小生成树的线段
(因为所需要花费的代价少于最小生成树的代价)。
我们可以先将最小生成树的线段列举出来,
然后将某一条边去掉,这样B的值确定,
然后确定A的值,即找两个集合各自最大的节点值。
//
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#define N 1005
using namespace std;
int fa[N];
double ans,answer,top;
struct node
{
int t,x,y;
int w;
}Node[N];
struct flow
{
int x;int y;
double len;
}Flow[N*500],flow1[N];
int findx(int x)
{
return G[x]==x?x:G[x]=findx(G[x]);
}
double f(int i,int j)
{
double p=(Node[i].x-Node[j].x)*(Node[i].x-Node[j].x);
double q=(Node[i].y-Node[j].y)*(Node[i].y-Node[j].y);
return sqrt(p+q);
}
bool cmp(flow a,flow b)
{
return a.len<b.len;
}
bool compare(node a,node b)
{
return a.w>b.w;
}
int main()
{
int T,n,i,j,r;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
for(i=0;i<n;i++)
{
scanf("%d %d %d",&Node[i].x,&Node[i].y,&Node[i].w);
Node[i].t=i;
fa[i]=i;
}
int t=0;
for(i=0;i<n;i++)
for(j=i+1;j<n;j++)
{
Flow[t].x=i;
Flow[t].y=j;
Flow[t].len=f(i,j);
t++;
}
sort(Flow,Flow+t,cmp);
ans=0,r=0,top=0;
//先求出最小生成树
for(i=0;i<t;i++)
{
int p=findx(Flow[i].x);
int q=findx(Flow[i].y);
if(p!=q)
{
flow1[r].x=Flow[i].x;
flow1[r].y=Flow[i].y;
flow1[r].len=Flow[i].len;//flow1存最小生成树的点
r++;
ans+=Flow[i].len;
fa[p]=q;
}
}
sort(Node,Node+n,compare);
for(i=0;i<r;i++)
{
ans-=flow1[i].len;//然后枚举最小生成树的路径删除
for(j=0;j<n;j++) fa[j]=j;
for(j=0;j<r;j++)//去掉一边后,有两个集合
{
if(j==i) continue;
int p=findx(flow1[j].x);
int q=findx(flow1[j].y);
if(p!=q)
{
fa[p]=q;
}
}
for(j=1;j<n;j++)
{
if(findx(Node[j].t)!=findx(Node[0].t))//两个不同集合
{
answer=(double)(Node[0].w+Node[j].w)/ans;//由于Node大到小排序
if(top<answer) top=answer;//Node【0】集合1最大,Node【j】集合2最大
break;
}
}
ans+=flow1[i].len;
}
printf("%.2f\n",top);
}
return 0;
}