朱刘算法 有向图定根的最小生成树poj3164

关于为什么不能用Prim求解此类问题,如下

Prim可以看成是维护两个顶点集或者看成维护一颗不断生成的树(感觉前一种说法好一点)

倘若是有向图有三个顶点1.2.3

边的情况如下

1->2:          5

1->3:         6

2->3:       1000861

3->2:         2

显然若是按照Prim算法来说,先将顶点一压入集合。而后顺势找到最小的顶点2,然后1.2中到三的最短边是1000861,那么花费就是1000866,显然不对的。;

而若是无向图则没有这个问题(也就是是说无向图保证最小就是最优),无向图的话找到2,花费5,然后顺势找到3,花费2,这是对的。

以下为转载内容:http://blog.csdn.net/lynnucas/article/details/51305910

最小生成树的具体问题可以用下面的语言阐述:
    输入:一个无向带权图G=(V,E),对于每一条边(u, v)属于E,都有一个权值w。

    输出:这个图的最小生成树,即一棵连接所有顶点的树,且这棵树中的边的权值的和最小。

  举例如下,求下图的最小生成树:

  这个问题是求解一个最优解的过程。那么怎样才算最优呢?

  首先我们考虑最优子结构:如果一个问题的最优解中包含了子问题的最优解,则该问题具有最优子结构。

  最小生成树是满足最优子结构的,下面会给出证明:

  最优子结构描述:假设我们已经得到了一个图的最小生成树(MST) T,(u, v)是这棵树中的任意一条边。如图所示:

  现在我们把这条边移除,就得到了两科子树T1和T2,如图:

  T1是图G1=(V1, E1)的最小生成树,G1是由T1的顶点导出的图G的子图,E1={(x, y)∈E, x, y ∈V1}

  同理可得T2是图G2=(V2, E2)的最小生成树,G2是由T2的顶点导出的图G的子图,E2={(x, y)∈E, x, y ∈V2}

  现在我们来证明上述结论:使用剪贴法。w(T)表示T树的权值和。

    首先权值关系满足:w(T) = w(u, v)+w(T1)+w(T2)

    假设存在一棵树T1‘比T1更适合图G1,那么就存在T‘={(u,v)}UT1‘UT2‘,那么T‘就会比T更适合图G,这与T是最优解相矛盾。得证。

  因此最小生成树具有最优子结构,那么它是否还具有重叠子问题性质呢?我们可以发现,不管删除那条边,上述的最优子结构性质都满足,都可以同样求解,因此是满足重叠子问题性质的。

  考虑到这,我们可能会想:那就说明最小生成树可以用动态规划来做咯?对,可以,但是它的代价是很高的。

  我们还能发现,它还有个更强大的性质:贪心选择性质。因而可用贪心算法完成。

  贪心算法特点:一个局部最优解也是全局最优解。

  最小生成树的贪心选择性质:令T为图G的最小生成树,另A?V,假设边(u, v)∈E是连接着A到A的补集(也就是V-A)的最小权值边,那么(u, v)属于最小生成树。

  证明:假设(u, v)?T, 使用剪贴法。现在对下图进行分析,图中A的点用空心点表示,V-A的点用实心点表示:

  在T树中,考虑从u到v的一条简单路径(注意现在(u, v)不在T中),根据树的性质,它是唯一的。

    现在把(u, v)和这条路上中的第一条连接A和V-A的边交换,即画红杠的那条边,边(u, v)是连接A和V-A的权值最小边,那我们就得到了一棵更小的树,这就与T是最小  生成树矛盾。得证。

  现在呢,我们来看看Prim的思想:Prim算法的特点是集合E中的边总是形成单棵树。树从任意根顶点s开始,并逐渐形成,直至该树覆盖了V中所有顶点。每次添加到树中的边都是使树的权值尽可能小的边。因而上述策略是“贪心”的。

我是大自然的搬运工 -----http://blog.csdn.net/wsniyufang/article/details/6747392

最 小树形图,就是给有向带权图中指定一个特殊的点root,求一棵以root为根的有向生成树T,并且T中所有边的总权值最小。最小树形图的第一个算法是 1965年朱永津和刘振宏提出的复杂度为O(VE)的算法。
判断是否存在树形图的方法很简单,只需要以v为根作一次图的遍历就可以了,所以下面的 算法中不再考虑树形图不存在的情况。
在所有操作开始之前,我们需要把图中所有的自环全都清除。很明显,自环是不可能在任何一个树形图上的。只有进 行了这步操作,总算法复杂度才真正能保证是O(VE)。
首先为除根之外的每个点选定一条入边,这条入边一定要是所有入边中最小的。现在所有的最小 入边都选择出来了,如果这个入边集不存在有向环的话,我们可以证明这个集合就是该图的最小树形图。这个证明并不是很难。如果存在有向环的话,我们就要将这 个有向环所称一个人工顶点,同时改变图中边的权。假设某点u在该环上,并设这个环中指向u的边权是in[u],那么对于每条从u出发的边(u, i, w),在新图中连接(new, i, w)的边,其中new为新加的人工顶点; 对于每条进入u的边(i, u, w),在新图中建立边(i, new, w-in[u])的边。为什么入边的权要减去in[u],这个后面会解释,在这里先给出算法的步骤。然后可以证明,新图中最小树形图的权加上旧图中被收缩 的那个环的权和,就是原图中最小树形图的权。
上面结论也不做证明了。现在依据上面的结论,说明一下为什么出边的权不变,入边的权要减去in [u]。对于新图中的最小树形图T,设指向人工节点的边为e。将人工节点展开以后,e指向了一个环。假设原先e是指向u的,这个时候我们将环上指向u的边 in[u]删除,这样就得到了原图中的一个树形图。我们会发现,如果新图中e的权w‘(e)是原图中e的权w(e)减去in[u]权的话,那么在我们删除 掉in[u],并且将e恢复为原图状态的时候,这个树形图的权仍然是新图树形图的权加环的权,而这个权值正是最小树形图的权值。所以在展开节点之后,我们 得到的仍然是最小树形图。逐步展开所有的人工节点,就会得到初始图的最小树形图了。
如果实现得很聪明的话,可以达到找最小入边O(E),找环 O(V),收缩O(E),其中在找环O(V)这里需要一点技巧。这样每次收缩的复杂度是O(E),然后最多会收缩几次呢?由于我们一开始已经拿掉了所有的 自环,我门可以知道每个环至少包含2个点,收缩成1个点之后,总点数减少了至少1。当整个图收缩到只有1个点的时候,最小树形图就不不用求了。所以我们最 多只会进行V-1次的收缩,所以总得复杂度自然是O(VE)了。由此可见,如果一开始不除去自环的话,理论复杂度会和自环的数目有关。
======================== 分割线之上摘自Sasuke_SCUT的blog=====================================================

下 面是朱刘算法的构造图

#include <cstdio>
#include <iostream>
#include<queue>
#include<set>
#include<ctime>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<cstring>
using namespace std;
const double eps=1e-10;
#define M 109
#define type double
const type inf=(1)<<30;
struct point
{
    double x,y;
}p[M];
double dis(point a,point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
struct Node{
    int u , v;
    type cost;
}E[M*M+5];
int pre[M],ID[M],vis[M];
type In[M];
int n,m;
type Directed_MST(int root,int NV,int NE) {
    type ret = 0;
    while(true) {
        //1.找最小入边
        for(int i=0;i<NV;i++) In[i] = inf;
        for(int i=0;i<NE;i++){
            int u = E[i].u;
            int v = E[i].v;
            if(E[i].cost < In[v] && u != v) {
                pre[v] = u;
                In[v] = E[i].cost;
            }
        }
        for(int i=0;i<NV;i++) {
            if(i == root) continue;
            if(In[i] == inf)    return -1;//除了跟以外有点没有入边,则根无法到达它
        }
        //2.找环
        int cntnode = 0;
    //    CC(ID,-1);
    //    CC(vis,-1);
    memset(ID,-1,sizeof(ID));
    memset(vis,-1,sizeof(vis));
        In[root] = 0;
        for(int i=0;i<NV;i++) {//标记每个环
            ret += In[i];
            int v = i;
            while(vis[v] != i && ID[v] == -1 && v != root) {
                vis[v] = i;
                v = pre[v];
            }
            if(v != root && ID[v] == -1) {
                for(int u = pre[v] ; u != v ; u = pre[u]) {
                    ID[u] = cntnode;
                }
                ID[v] = cntnode ++;
            }
        }
        if(cntnode == 0)    break;//无环
        for(int i=0;i<NV;i++) if(ID[i] == -1) {
            ID[i] = cntnode ++;
        }
        //3.缩点,重新标记
        for(int i=0;i<NE;i++) {
            int v = E[i].v;
            E[i].u = ID[E[i].u];
            E[i].v = ID[E[i].v];
            if(E[i].u != E[i].v) {
                E[i].cost -= In[v];
            }
        }
        NV = cntnode;
        root = ID[root];
    }
    return ret;
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        // memset(pre,0,sizeof(pre));
        for(int i=0;i<n;i++)
        scanf("%lf%lf",&p[i].x,&p[i].y);
        for(int i=0;i<m;i++)
        {
        scanf("%d%d",&E[i].u,&E[i].v);
        E[i].u--;
        E[i].v--;
        if(E[i].u!=E[i].v)
        E[i].cost=dis(p[E[i].u],p[E[i].v]);
        else E[i].cost=1<<30;
        }
        type ans=Directed_MST(0,n,m);
        if(ans==-1)
        printf("poor snoopy\n");
        else
        printf("%.2f\n",ans);
    }
    return 0;
}
时间: 2024-08-03 06:59:11

朱刘算法 有向图定根的最小生成树poj3164的相关文章

hdu2121 - Ice_cream’s world II(朱刘算法,不固定根)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121 题目意思大概是要你在一些城市中选一个做首都 , 要求首都都能到其他城市 , 道路花费要最少 , 且道路都是单向的 , 这个时候就要用到最小树形图算法了 , 而且是不固定根. 不定根就是加一个虚根(原本不存在的点) , 可以让这个虚根到每个点的距离大于原本所有点连接的道路花费之和sum , 然后计算出的结果减去sum,如果比sum还大就可以认为通过这个虚拟节点我们连过原图中两个点,即原图是不连通

POJ3164 Command Network【最小树形图】【朱刘算法】

Command Network Time Limit: 1000MS Memory Limit: 131072K Total Submissions: 13398 Accepted: 3868 Description After a long lasting war on words, a war on arms finally breaks out between littleken's and KnuthOcean's kingdoms. A sudden and violent assau

训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分)

layout: post title: 训练指南 UVA- 11865(有向最小生成树 + 朱刘算法 + 二分) author: "luowentaoaa" catalog: true mathjax: true tags: - 最小生成树 - 图论 - 训练指南 Stream My Contest UVA - 11865 二分带宽,然后判断最小生成树是否小于cost值. #include<bits/stdc++.h> using namespace std; typede

UVa11183 Teen Girl Squad, 最小树形图,朱刘算法

Teen Girl Squad Input: Standard Input Output: Standard Output You are part of a group of n teenage girls armed with cellphones. You have some news you want to tell everyone in the group. The problem is that no two of you are in the same room, and you

POJ--3164--Command Network【朱刘算法】最小树形图

链接:http://poj.org/problem?id=3164 题意:告诉n个点坐标,m条边表示两个点之间有路.从1点開始建立一个有向图最小生成树. 朱刘算法模板题 ========================== 切割线之下摘自user_id=Sasuke_SCUT" style="color:rgb(202,0,0); text-decoration:none; font-family:Arial; font-size:14px; line-height:26px"

HDUOJ--2121--Ice_cream’s world II【朱刘算法】不定根最小树形图

链接:http://acm.hdu.edu.cn/showproblem.php?pid=2121 题意:n个顶点,m条边,求从某一点起建立有向图最小生成树并且花费最小,输出最小花费和根节点下标. 思路:这道题根是不确定的,我们可以先假设一个根,从这个根出发到任何一点的距离(sum)都比原图总权值还大,这样保证了虚拟的边不会是最小入边,也为之后判断是否生成了最小树形图提供方便,从这个点开始建立最小树形图,最后生成出一个结果,很显然虚拟的根只有一条出边,并且出边连接的点就是真实的根. 最后得到的最

最小树形图 之 朱刘算法【模板】

定义:一个有向图,存在从某个点为根的,可以到达所有点的一个最小生成树,则它就是最小树形图. 朱刘算法实现过程: [在选出入边集后(看步骤1),若有向图中不存在有向环,说明该图就是最小树形图] 1,选入边集--找到除root点之外,每一个点的所有入边中权值最小的,用数组in[]记录下这个最小权值,用pre[]记录到达该点的前驱:(若图中存在独立点,最小树形图是不存在的,所以在该步骤结束后,要判断一下) 2,找有向环,并用数组id[]记录节点所属环的编号. 3,找到环后,缩点,并更新权值.(感觉和S

【POJ 3164】【朱刘算法模板】Command Network

Command Network Time Limit: 1000MS   Memory Limit: 131072K Total Submissions: 13977   Accepted: 4012 Description After a long lasting war on words, a war on arms finally breaks out between littleken's and KnuthOcean's kingdoms. A sudden and violent a

朱 - 刘算法

· 定义 对于有向无环图G (V, E),类似最小生成树的定义,有向图最小树形图即在有向图上查找总权值和最小的树形图(即有向边的树). · 朱 - 刘算法 对于每个点先选取到达它的最小的边,这样可组成一个边集E1,显然,该边集权值和最小,但不一定是树. 在该边集上进行缩点,并判断是否有解(是否有点无入度),在融会G中,记为G1. 当然,若此时没有找到有向环且有解,说明在当前图上已找到最小树形图,那么将原来的缩点解开,即除了当前树形图上的弧之外,将缩点内没有与已知弧有相同终点的边选出,如此构成了G