题意是求依次删除某条边之后的最小生成树里的最大值(与相连的边不能删除)。
可以先做MST,然后枚举MST中的边。删除边(u,v)之后会剩下2个子树,接下来需要找使得两个子树相连的最小边。这个可以用dp预处理出来,dp[u][v]表示删除边(u,v)之后以u为根的子树和以v为根的子树相连的最小边值。
O(n^2)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int maxn=1011;
struct edge
{
int u,v;
double w;
bool operator <(const edge& ths)const
{
return w<ths.w;
}
}e[maxn*maxn];
struct rec
{
int v;
double w;
};
int f[maxn];
double dp[maxn][maxn];
vector<rec>g[maxn];
double x[maxn],y[maxn];
bool vis[maxn],in[maxn*maxn];
double cal(double x,double y)
{
return sqrt(x*x+y*y);
}
int find(int x)
{
return f[x]<0?x:f[x]=find(f[x]);
}
double dfs(int u,int fa,int root)
{
vis[u]=true;
double res=1e20;
for(int i=0;i<g[u].size();i++)
if(!vis[g[u][i].v])
{
int v=g[u][i].v;
double tmp=dfs(v,u,root);
res=min(res,tmp);
dp[u][v]=dp[v][u]=min(dp[u][v],tmp);
}
if(fa!=root)//!!
res=min(res,cal(x[u]-x[root],y[u]-y[root]));
return res;
}
int main()
{
//freopen("data","r",stdin);
int sec;
scanf("%d",&sec);
while(sec--)
{
int n;double k;
scanf("%d%lf",&n,&k);
for(int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
//
int m=0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
{
m++;
e[m].u=i;
e[m].v=j;
e[m].w=cal(x[i]-x[j],y[i]-y[j]);
//cout<<i<<" "<<j<<" "<<e[m].w<<endl;
}
//MST
for(int i=1;i<=m;i++)
in[i]=false;
for(int i=1;i<=n;i++)
{
f[i]=-1;
g[i].clear();
}
sort(e+1,e+1+m);
double ans=0;
int num=0;
for(int i=1;i<=m;i++)
{
int fu=find(e[i].u);
int fv=find(e[i].v);
if(fu!=fv)
{
f[fu]=fv;
num++;
ans+=e[i].w;
rec tmp;tmp.v=e[i].v;tmp.w=e[i].w;
g[e[i].u].push_back(tmp);
tmp.v=e[i].u;g[e[i].v].push_back(tmp);
in[i]=true;
if(num==n-1)break;
}
}
//DP
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
dp[i][j]=1e20;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
vis[j]=false;
dfs(i,i,i);
}
//solve
double solve=ans;
for(int i=1;i<=m;i++)
if(in[i])
{
int u=e[i].u,v=e[i].v;
if(u==1 || v==1)continue;
double w=e[i].w;
solve=max(solve,ans-w+dp[u][v]);
}
solve=solve*k;
printf("%.2f\n",solve);
}
return 0;
}