题目链接
题目大意
给定n个点的点权及相互间的边权,求一棵树,其中一条边的边权变为0,树的比率值为该0值边所连的两点的点权和/剩下的树边和。
求这个值最大是多少。
题目思路
这题要用到次小生成树的思想,即找到最小生成树,然后添加一条边构成环,再删掉环中属于最小树的最大边,用这种方法遍历所有边以
找到最终长度。
易错警示
1:注意初始化的时候要仔细
2:注意边长double类型
3:求i到j的所有路线中最长的线段,dfs函数有点玄学(可能是我太菜的原因
代码
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=1e3+3;
int t,n,a[maxn],b[maxn],p[maxn],cnt,fa[maxn];
double d[maxn][maxn],ans,sum;//注意为double类型
vector<int>dis[maxn];
bool vis[maxn];
struct node
{
int l,r;
double len;
}e[maxn*maxn];
bool cmp(node a,node b)
{
return a.len<b.len;
}
int findd(int x)
{
if(x==fa[x])
return x;
return fa[x]=findd(fa[x]);
}
void dfs(int l,int mid)//最关键也是最难的函数
{
vis[mid]=1;//到了就标记
for(int i=0;i<dis[mid].size();i++)
{
int r=dis[mid][i];
if(vis[r]==0)//没走过
{
d[l][r]=max(d[l][mid],d[mid][r]);
dfs(l,r);
}
}
}
int main()
{
scanf("%d",&t);
while(t--)
{
cnt=0,ans=0.0,sum=0.0;//初始化
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
dis[i].clear();//这个清空千万别忘了
fa[i]=i;
scanf("%d%d%d",&a[i],&b[i],&p[i]);
}
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
cnt++;
e[cnt].l=i,e[cnt].r=j;
e[cnt].len=sqrt((a[i]-a[j])*(a[i]-a[j])+(b[i]-b[j])*(b[i]-b[j]));
}
}
sort(e+1,e+1+cnt,cmp);
for(int i=1;i<=cnt;i++)//求最小生成树
{
int x=e[i].l;
int y=e[i].r;
if(findd(x)!=findd(y))
{
fa[findd(x)]=findd(y);
dis[x].push_back(y);
dis[y].push_back(x);
sum=sum+e[i].len;
d[x][y]=e[i].len;
d[y][x]=e[i].len;
}
}
for(int i=1;i<=n;i++)//查找i到j的最长的一条线段
{
memset(vis,0,sizeof(vis));
dfs(i,i);
}
for(int i=1;i<n;i++)
{
for(int j=i+1;j<=n;j++)
{
ans=max(ans,1.0*(p[i]+p[j])/(sum-d[i][j]));
}
}
printf("%.2f\n",ans);
}
return 0;
}