有向圖的最小生成樹,最小樹形圖

轉載:

有固定根的最小樹形圖求法O(VE):

首先消除自環,顯然自環不在最小樹形圖中。然後判定是否存在最小樹形圖,以根爲起點DFS一遍即可。

之後進行以下步驟。

設cost爲最小樹形圖總權值。
0.置cost=0。
1.求最短弧集合Ao (一條弧就是一條有向邊)

除源點外,爲所有其他節點Vi,找到一條以Vi爲終點的邊,把它加入到集合Ao中。

(加邊的方法:所有點到Vi的邊中權值最小的邊即爲該加入的邊,記prev[vi]爲該邊的起點,mincost[vi]爲該邊的權值)

2.檢查Ao中的邊是否會形成有向圈,有則到步驟3,無則到步驟4。

(判斷方法:利用prev數組,枚舉爲檢查過的點作爲搜索的起點,做類似DFS的操作)

3.將有向環縮成一個點。
假設環中的點有(Vk1,Vk2,… ,Vki)總共i個,用縮成的點叫Vk替代,則在壓縮後的圖中,其他所有不在環中點v到Vk的距離定義如下:
gh[v][Vk]=min { gh[v][Vkj]-mincost[Vkj] } (1<=j<=i)而Vk到v的距離爲
gh[Vk][v]=min { gh[Vkj][v] }              (1<=j<=i)
同時注意更新prev[v]的值,即if(prev[v]==Vkj) prev[v]=Vk
另外cost=cost+mincost[Vkj] (1<=j<=i)

到步驟1.

4.cost加上Ao的權值和即爲最小樹形圖總權值。

如要輸出最小樹形圖較煩,沒實現過。

找環O(V),收縮O(E),總複雜度O(VE)。

那幅對我有莫大幫助的流程圖如下,

對於不固定根的最小樹形圖,wy教主有一巧妙方法。摘錄如下:
新加一個點,和每個點連權相同的邊,這個權大於原圖所有邊權的和,這樣這個圖固定跟的最小樹形圖和原圖不固定跟的最小樹形圖就是對應的了。
代碼:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define maxn 120
#define INF 99999999999.0
int n,m;
struct node
{
    double x,y;
}a[maxn];
inline double get_dis(node a,node b)
{
    double x=a.x-b.x;
    double y=a.y-b.y;
    return sqrt(x*x+y*y);
}
double map[maxn][maxn];
bool flag[maxn];
int pre[maxn];
void dfs(int x)
{
    flag[x]=true;
    for(int i=1;i<=n;i++)if(!flag[i]&&map[x][i]!=INF)
        dfs(i);
}
bool check()
{
    memset(flag,0,sizeof(flag));
    dfs(1);
    for(int i=1;i<=n;i++)
        if(!flag[i])return false;
    return true;
}
double solve()
{
    memset(flag,0,sizeof(flag));//flag是true的點是被去掉的點
    int i,j,k;
    double ans=0;
    while(1)
    {
        for(i=2;i<=n;i++)if(!flag[i])
        {
            pre[i]=i;
            map[i][i]=INF;
            for(j=1;j<=n;j++)if(!flag[j])
            {
                if(map[pre[i]][i]>map[j][i])
                    pre[i]=j;
            }
        }
        for(i=2;i<=n;i++)if(!flag[i])
        {
            bool mark[maxn];
            memset(mark,0,sizeof(mark));
            for(j=i;j!=1&&!mark[j];mark[j]=true,j=pre[j]);//尋找環,返回在環內的一點(注意從i出發能找到換不代表n在環內)
            if(j==1)continue;
            i=j;
            ans+=map[pre[i]][i];
            for(j=pre[i];j!=i;j=pre[j])
            {
                ans+=map[pre[j]][j];
                flag[j]=true;
            }
            for(j=1;j<=n;j++)if(!flag[j]&&map[j][i]!=INF)
                map[j][i]-=map[pre[i]][i];
            for(j=pre[i];j!=i;j=pre[j])
            {
                for(k=1;k<=n;k++)if(!flag[k]&&map[j][k]!=INF)
                    map[i][k]=min(map[i][k],map[j][k]);
                for(k=1;k<=n;k++)if(!flag[k]&&map[k][j]!=INF)
                    map[k][i]=min(map[k][i],map[k][j]-map[pre[j]][j]);
            }
            break;
        }
        if(i>n)//說明沒有環了。
        {
            for(j=2;j<=n;j++)if(!flag[j])
                ans+=map[pre[j]][j];
            return ans;
        }
    }
}
int main()
{
    int i,j,x,y;
    while(scanf("%d%d",&n,&m)!=-1)
    {
        for(i=1;i<=n;i++)
            scanf("%lf%lf",&a[i].x,&a[i].y);
        for(i=1;i<=n;i++)
            for(j=1;j<=n;j++)
                map[i][j]=INF;
        for(i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            if(x==y)continue;//消除自環
            map[x][y]=get_dis(a[x],a[y]);
        }
        if(!check())//檢查有向圖是否聯通
        {
            printf("poor snoopy\n");
        }
        else
        {
            printf("%.2lf\n",solve());
        }
    }
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章