题意:给你一个n个点的坐标,之间两两相连,其中2~n-1形成的边中有一条边不能用,问你最小生成树的最大值。
想法:当对原图求最小生成树之后,记录这一棵树,当我们删除一条边之后,最小生成树就分成了A,B两个连通分量,则需要保存当删除这条边是A到B的最短距离。
小技巧:现在的问题就是:删除一边,求A到B的最短距离?
最笨的方法肯定太耗时间了,就是枚举每一条边然后跑spfa那么时间复杂度就是O(edges^3),但是通过树形dp,枚举每一个点那么时间复杂度O(n^2),又是因为edges=n^2。把枚举的点当成是root点,也就是规定有root的A到B一定是root->B中的点最短。dp[u][v]表示删除uv这条边两个连通块的最短距离。
还有一点要注意的是与root相连的点是不可以更新的,因为root->v是要删除的最小生成树的边,而要找的是除它以外的边。
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #define inf 0x7fffffff using namespace std; int n,k; double map[1000+50][1000+50],dp[1000+50][1000+50]; int used[1000+50][1000+50],pre[1000+50]; double ans; struct node { int x,y; }dir[1000+50]; struct nodee { int v,next; }e[2000+50]; int head[1000+50],cnt; void add(int a,int b) { e[cnt].v=b; e[cnt].next=head[a]; head[a]=cnt++; } void Input() { scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) { scanf("%d%d",&dir[i].x,&dir[i].y); } } double Max(double a,double b) { if(a>b) return a; else return b; } double Min(double a,double b) { if(a<b) return a; else return b; } double dis(node a,node b) { double k=sqrt((double)(a.x-b.x)*(double)(a.x-b.x)+(double)(a.y-b.y)*(double)(a.y-b.y)); return k; } void build_map() { for(int i=1;i<=n;i++) { for(int j=i;j<=n;j++) { map[i][j]=map[j][i]=dis(dir[i],dir[j]); dp[i][j]=dp[j][i]=(double)inf; } } } void prime() { int vis[1000+5]; double low[1000+5]; for(int i=1;i<=n;i++) { low[i]=map[1][i]; pre[i]=1; vis[i]=0; } low[1]=0; vis[1]=1; pre[1]=-1; for(int i=2;i<=n;i++) { double min=inf; int pos; for(int j=1;j<=n;j++) { if(!vis[j]&&min>low[j]) { min=low[j]; pos=j; } } vis[pos]=1; ans+=low[pos]; used[pre[pos]][pos]=used[pos][pre[pos]]=1; add(pre[pos],pos);add(pos,pre[pos]); for(int j=1;j<=n;j++) { if(!vis[j]&&map[pos][j]<low[j]) { low[j]=map[pos][j]; pre[j]=pos; } } } } double dfs(int root,int u,int fa) { double res=(double)inf; for(int i=head[u];i+1;i=e[i].next) { int v=e[i].v; if(v==fa) continue; double tmp=dfs(root,v,u); dp[u][v]=dp[v][u]=Min(tmp,dp[u][v]); res=Min(res,tmp); } if(root!=fa) { res=Min(map[root][u],res); } return res; } void treatment() { memset(used,0,sizeof(used)); memset(head,-1,sizeof(head)); cnt=0; ans=0; prime(); double Ans=ans; for(int i=1;i<=n;i++) { dfs(i,i,-1); } for(int i=2;i<n;i++) { for(int j=i+1;j<=n;j++) { if(used[i][j]) { Ans=Max(Ans,ans+dp[i][j]-map[i][j]); } } } printf("%.2lf\n",Ans*k); } int main() { int t; scanf("%d",&t); while(t--) { Input(); build_map(); treatment(); } return 0; }