天天看點

poj 3164 <朱劉算法《模闆》求最小樹形圖>

題目連結:  ​​poj 3164​​

有向圖的最小樹形圖:此算法由朱永津和劉振宏在1965年發表。

膜拜--

寫下自己了解的思路---    一天一道題----痛并快樂着--

解題步驟:

1》先從根節點DFS一下,看是否有解--

2》尋找除根節點外的所有點的最小入邊--

3》從所有點向上尋找--看中間路徑是否存在環--(如果不存在環,說明已經找到最小樹形圖--将各邊加入ans中--算法結束)

4》如果存在環的話就先将環上各邊之和加入ans--然後找各點的其他入邊并更新(因為已将最小入邊加上--其他入邊以後如果還用時就應該用它與最小入邊的內插補點)

---然後-收縮環為環中一點--除收縮點外,其他點與外界的狀态全部轉移到收縮點--其他點标記一下(隐藏)--然後傳回步驟2.

圖文可在百度文庫下載下傳:​​最小樹形圖​​

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm> 
using namespace std;
int n,m;
#define M 999999999
double x[120],y[120];
double map[120][120];
bool fafe[120];
double dist(int xx,int yy)
{
  double lp=sqrt((x[xx]-x[yy])*(x[xx]-x[yy])+(y[xx]-y[yy])*(y[xx]-y[yy]));
  return lp;
}
int fer[120];
double zhuliu()
{
  int pre[120],i;
  bool visit[120]; 
  double ans=0;
  for (int i=1;i<=n;i++)
  {
    fafe[i]=false;
    map[i][i]=M;
  }
  pre[1]=1;
  while (true)
  {
    for (i=2;i<=n;i++)
    {
      if (fafe[i])continue;
      pre[i]=i;
      for (int j=1;j<=n;j++)
      {
        if (!fafe[j]&&map[j][i]<map[pre[i]][i])
        pre[i]=j;
      }
    }
    for (i=2;i<=n;i++)
    {
      if (fafe[i])continue;
      memset(visit,false,sizeof(visit));
      visit[1]=true;
      int j=i;
      do
      {
        visit[j]=true;
        j=pre[j];
      }while(!visit[j]);
      if (j==1) continue;//從根節點出發--到i之間無環。
      i=j;/*這裡是i=j,,,寫成j=i就逾時--- 如果有環時j不是回到i了嗎?? 
      原來是從i想上找---循環可能沒有i---如:3——4---5----4:pre[3]=4--pre[4]=5---pre[5]=4;
      這樣的話--如果下面的循環還是從i開始--但是判斷結果是就j!=i--永遠會不到3的位置--就會 Time Limit Exceeded*/
      do
      {
        ans+=map[pre[j]][j];
        j=pre[j];
      }while (j!=i);
      j=i;
      do//改變權值 
      {
        for (int k=1;k<=n;k++)
        {
          if (fafe[k]) continue;
          if (map[k][j]<M&&k!=pre[j])
          map[k][j]-=map[pre[j]][j];
        }
        j=pre[j];
      }while (j!=i);
      for (j=1;j<=n;j++)//将與環上的點相連的路全部連在i上 
      {
        if (j==i) continue;
        for (int k=pre[i];k!=i;k=pre[k])
        {
          if (map[k][j]<map[i][j]) map[i][j]=map[k][j];
          if (map[j][k]<map[j][i]) map[j][i]=map[j][k];
        }
      }
      for (j=pre[i];j!=i;j=pre[j])//除i點,其他點隐藏 
      fafe[j]=true;
      break;// 此環已壓縮--當做一點--重新找每點的最短入邊-- 
    }
    if (i==n+1)//從2到n都無環--- 
    {
      for (int j=2;j<=n;j++)
      if (!fafe[j])
      ans+=map[pre[j]][j];
      break;
    }
  }
  return ans;
}
void DFS(int xx)
{
  fafe[xx]=false;
  for (int i=1;i<=n;i++)
  if (fafe[i]&&map[xx][i]!=M)
  DFS(i);
  return ;
}
int main()
{
  while (~scanf("%d%d",&n,&m))
  {
    for (int i=1;i<=n;i++)
    for (int j=1;j<=n;j++)
    map[i][j]=M;
    for (int i=1;i<=n;i++)
    scanf("%lf%lf",&x[i],&y[i]);
    int a,b;
    for (int i=0;i<m;i++)
    {
      scanf("%d%d",&a,&b);
      if (a==b) continue;
      map[a][b]=dist(a,b);
    }
    bool falg=true;
    memset(fafe,true,sizeof(fafe));
    DFS(1);
    for (int i=1;i<=n;i++)
    if (fafe[i])
    {
      falg=false;
      break;
    }
    if (falg)
    printf("%.2lf\n",zhuliu());
    else
    printf("poor snoopy\n");
  }
  return 0;
}