HDU 4756 Install Air Conditioning 块与块,非树边最短路

题意:给你一个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;
} 


你可能感兴趣的:(块块最短距离)