之前学习过这个算法,后来没有复习,昨天上海网络赛的C题就出现了,而在OJ上的例子也不多。当时一味的同Kruska去解,头都大了,完全忘记了有向图应该用最小树形图。昨晚回头来看,还看了一段时间才明白,真是,只怪自己做题的态度吧,这下边是一个别人写得代码,也不记得再哪儿转的了,没出处,不好意思,只想用来阐述这个算法。
#include <stdio.h>
#include <string.h>
#include <math.h>
double gh[101][101], mincost[101],ans;
int prev[101],stack[101],id[101],mark[101],numv,nume,cnt,top,sta,
x[101],y[101];
int scaned[101];
void Combine() {
int i,j,now,x;
double t;
now=stack[sta];//now为当前缩点的编号,stack记录了环上的点
for (i=sta;i<top;i++)
{
x=stack[i];//x是环中的点
ans+=mincost[x];//首先得加上环上所有最小入度,后边再加上那个最小差值就好
id[x]=now;//改变环上所有点的id
for (j=1;j<=numv;j++)
{
if (gh[j][x]!=-1)
{
t=gh[j][x]-mincost[x];//所有其他边到当前集合的边权-最小的权==当前缩点的入度
if (t<gh[j][now] || gh[j][now]==-1) gh[j][now]=t;
}
if ((gh[x][j]!=-1 && gh[x][j]<gh[now][j]) || gh[now][j]==-1) gh[now][j]=gh[x][j];//更新处理出度
}
}
for (i=2;i<=numv;i++) //id[i]==i是那些没在环上的点
if (id[i]==i) prev[i]=id[prev[i]];//更新处理路径
mincost[now]=1e20;
for (i=1;i<=numv;i++)
if (i!=now && id[i]==i && gh[i][now]!=-1 && gh[i][now]<mincost[now]) //求出更新后的最小入度,记录路径
mincost[now]=gh[i][now],prev[now]=i;
}
int Find_Circle() {
int i,temp,mark[101];
memset(scaned,0,sizeof(scaned));
for (i=numv;i>1;i--)
{
if (id[i]!=i || scaned[i]) continue;
memset(mark,0,sizeof(mark));
top=1;
temp=i;
while (temp!=1 && !mark[temp] && !scaned[temp])
{
scaned[temp]=1;
mark[temp]=top;
stack[top++]=temp;
temp=prev[temp];
}
if (mark[temp]) //如果在这些边集中找到坏
{
sta=mark[temp];
return 1;
}
}
return 0;
}
void DFS(int v) {
int i;
cnt++;
scaned[v]=1;
for (i=1;i<=numv;i++) if (!scaned[i] && gh[v][i]!=-1) DFS(i);
}
void Minimum_Arborescence () {
int i;
while (Find_Circle()) Combine();
for (i=2;i<=numv;i++)
if (i==id[i]) ans+=mincost[i];
}
int main () {
int i,j,v1,v2,val;
while (scanf("%d%d",&numv,&nume)!=EOF) {
for (i=1;i<=numv;i++) for (j=1;j<=numv;j++) gh[i][j]=-1;
memset(scaned,0,sizeof(scaned));
for (i=1;i<=numv;i++) scanf("%d%d",&x[i],&y[i]);
for (i=0;i<nume;i++) {
scanf("%d%d",&v1,&v2);
gh[v1][v2]=sqrt((x[v1]-x[v2])*(x[v1]-x[v2])+(y[v1]-y[v2])*(y[v1]-y[v2]));
}
cnt=0;
DFS(1);
if (cnt<numv) printf("poor snoopy\n"); //不连通
else {
id[1]=1;
for (i=2;i<=numv;i++) {
id[i]=i;
mincost[i]=1e20;
for (j=1;j<=numv;j++)
if(i!=j && gh[j][i]!=-1 && gh[j][i]<mincost[i]) //找到所有点的各自最小入边,记录路径
mincost[i]=gh[j][i],prev[i]=j;
}
ans=0;
Minimum_Arborescence();
printf("%.2lf\n",ans);
}
}
return 0;
}