hdu4576
题目
就是有一个发电站,几个寝室,在把他们连起来的最小生成树中,有一条边是不能连的,但是不知道,删去那条边后增加一条边可以得到一个次小生成树,现在就问所有的次小生成树中花费最大的是多少。
思路
先用prim求出最小生成树,然后树形dp求出两个点之间的最小替换边,然后枚举最大值。
代码
#include <iostream>
#include <cstring>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn=1010;
const int inf=0x3f3f3f3f;
int head[maxn],tot;
int n,k;
double dis[maxn][maxn],dp[maxn][maxn];
int x[maxn],y[maxn];
double sum=0;
int ma[maxn][maxn];
double f(int a,int b,int c,int d)
{
return sqrt((double)(a-b)*(a-b)+(double)(c-d)*(c-d));
}
struct node
{
int next;
int to;
} edge[maxn*2];
void addedge(int from,int to)
{
edge[tot].to=to;
edge[tot].next=head[from];
head[from]=tot++;
}
void prim()
{
int pre[maxn],vis[maxn];
double len[maxn];
for(int i=1; i<=n; i++)
{
pre[i]=1;
vis[i]=0;
len[i]=dis[i][1];
}
vis[1]=1;
for(int i=1; i<n; i++)
{
int id;
double minn=inf;
for(int j=2; j<=n; j++)
{
if(!vis[j])
{
if(minn>len[j])
{
minn=len[j];
id=j;
}
}
}
ma[pre[id]][id]=ma[id][pre[id]]=1;
sum+=len[id];
addedge(pre[id],id);
addedge(id,pre[id]);
vis[id]=1;
for(int j=2; j<=n; j++)
{
if(!vis[j]&&len[j]>dis[j][id])
{
len[j]=dis[j][id];
pre[j]=id;
}
}
}
}
double dfs(int cur,int u,int fa)
{
double ans=inf;
for(int i=head[u]; ~i; i=edge[i].next)
{
int v=edge[i].to;
if(v==fa) continue;
double temp=dfs(cur,v,u);
ans=min(temp,ans);
dp[u][v]=dp[v][u]=min(dp[u][v],temp);
}
if(cur!=fa)
ans=min(ans,dis[cur][u]);
return ans;
}
int main ()
{
int T;
scanf("%d",&T);
while(T--)
{
scanf("%d %d",&n,&k);
tot=0;
sum=0;
memset(head,-1,sizeof(head));
memset(ma,0,sizeof(ma));
for(int i=1; i<=n; i++)
{
scanf("%d %d",&x[i],&y[i]);
}
for(int i=1; i<=n; i++)
for(int j=i; j<=n; j++)
{
dis[i][j]=dis[j][i]=f(x[i],x[j],y[i],y[j]);
dp[i][j]=dp[j][i]=inf;
}
prim();
for(int i=1; i<=n; i++) dfs(i,i,-1);
double ans=sum;
for(int i=2; i<=n; i++)
for(int j=2; j<i; j++)
{
if(!ma[i][j]) continue;
ans=max(ans,sum-(dis[i][j]-dp[i][j]));
}
printf("%.2lf\n",ans*k);
}
return 0;
}