最小树形图模板题,用朱刘算法
注意自环和重边的处理
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=100+10;
const double INF=9999999999.9;
double g[N][N]; //邻接矩阵,不连通初始化为INF
bool vis[N],flag[N]; //flag缩点标记
int pre[N]; //前驱
double zhuliu(int root,int n)
{
double sum=0;
memset(flag,false,sizeof(flag));
pre[root]=root;
while(true)
{
for(int i=1;i<=n;i++) //求最短弧
{
if(flag[i]||i==root) continue;
pre[i]=i;
for(int j=1;j<=n;j++)
if(!flag[j]&&g[j][i]<g[pre[i]][i])
pre[i]=j;
if(pre[i]==i) return -1.0;
}
bool f=false;
for(int i=1;i<=n;i++) //检查每个点
{
if(flag[i]||i==root) continue;
memset(vis,false,sizeof(vis));
vis[root]=true;
int j=i;
do //找环
{
vis[j]=true;
j=pre[j];
}while(!vis[j]);
if(j==root) continue; //没有环
i=j;
f=true;
do //保存环内的值
{
sum+=g[pre[j]][j];
j=pre[j];
}while(j!=i);
j=i;
do //修改有关边的权值
{
for(int k=1;k<=n;k++)
if(!flag[k]&&g[k][j]<INF&&k!=pre[j])
g[k][j]-=g[pre[j]][j];
j=pre[j];
}while(j!=i);
for(int j=1;j<=n;j++) //缩点,缩为i号点,将边转移
{
if(j==i) continue;
for(int k=pre[i];k!=i;k=pre[k])
{
if(g[k][j]<g[i][j]) g[i][j]=g[k][j];
if(g[j][k]<g[j][i]) g[j][i]=g[j][k];
}
}
for(int j=pre[i];j!=i;j=pre[j]) flag[j]=true;
break;
}
if(!f)
{
for(int i=1;i<=n;i++)
if(!flag[i]&&i!=root) sum+=g[pre[i]][i];
break;
}
}
return sum;
}
double x[N],y[N];
int main()
{
int n,m;
int a,b;
while(~scanf("%d%d",&n,&m))
{
for(int i=1;i<=n;i++)
scanf("%lf%lf",&x[i],&y[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
g[i][j]=INF;
for(int i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
if(a==b) continue; //注意自环和重边
g[a][b]=min(g[a][b],sqrt((x[a]-x[b])*(x[a]-x[b])+(y[a]-y[b])*(y[a]-y[b])));
}
double ans=zhuliu(1,n);
if(ans==-1.0) printf("poor snoopy\n");
else printf("%.2f\n",ans);
}
return 0;
}