题意:
给出可以连接的两个点,并且是单向边,问最后最小生成树。
分析:
朱刘算法模板:
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int maxn=105;
#define INF 0x3f3f3f3f
double p[maxn][2];//坐标
int pre[maxn];//最短弧前驱
int vis[maxn];//点标记
int used[maxn];//查环过程的标记
double Map[maxn][maxn];//存图关系
int n,m,u,v,len;
double length(double x1,double y1,double x2,double y2){
return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
double zhuliu(int root)
{
double sum=0;
int i,j,k;
memset(vis,0,sizeof vis);
while(1){
for(i=1;i<=n;i++){ //1、求最短弧集合pre
if(vis[i]||i==root) continue;
pre[i]=i;
for(int j=1;j<=n;j++) //找i点的最短前驱弧
if(!vis[j]&&Map[j][i]<Map[pre[i]][i])
pre[i]=j;
if(pre[i]==i) return -1; //弱图不连通
}
for(i=1;i<=n;i++){ //2、查环
if(vis[i]||i==root) continue;
memset(used,0,sizeof used);
used[root]=1;
k=i;
while(!used[k]){
used[k]=1;
k=pre[k];
}
if(k!=root) break;//存在环
}
if(i>n){ //不存在环了
for(j=1;j<=n;j++)
if(j!=root&&!vis[j])
sum+=Map[pre[j]][j];
return sum;
}
i=k; //3、下面将这个环缩到i点;
do{ //4、先累加环记录下环权值
sum+=Map[pre[k]][k];
k=pre[k];
}while(k!=i);
do{//5、修改环上点的前驱边,为准备环收缩
for(j=1;j<=n;j++)
if(!vis[j]&&Map[j][k]<INF&&j!=pre[k])
Map[j][k]-=Map[pre[k]][k];
k=pre[k];
}while(k!=i);
for(j=1;j<=n;j++){ //6、环收缩到i点
if(j==i||vis[j])continue;
for(k=pre[i];k!=i;k=pre[k]){ //k点的对外距离给i点
if(Map[i][j]>Map[k][j])Map[i][j]=Map[k][j];
if(Map[j][i]>Map[j][k])Map[j][i]=Map[j][k];
}
}
for(k=pre[i];k!=i;k=pre[k])vis[k]=1;//7、将环上除i外全标记
}
}
int main()
{
while(~scanf("%d%d",&n,&m)){
for(int i=0;i<104;i++)
for(int j=0;j<104;j++)
Map[i][j]=INF*1.0;
for(int i=1;i<=n;i++)
scanf("%lf%lf",&p[i][0],&p[i][1]);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
if(u!=v) Map[u][v]=length(p[u][0],p[u][1],p[v][0],p[v][1]);
}
double ans=zhuliu(1);
if(ans<0) puts("poor snoopy");
else printf("%.2f\n",ans);
}
return 0;
}