【最小生成树专题】&论搞一棵树如何弄得复杂无比&

最小生成树专题


前言

因为老师满含微笑的喊我写所以我就写了
嗯嗯正式接触图论这一板块了,感觉不写点什么,有些对不起自己,所以就有了你们所看到的这篇博客
没有什么多的废话,赶紧赶紧地进入我们的博客里面来吧【很急诶→_→】!


第一题!-最优布线问题-为什么不用WIFI

题目描述

学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们中间有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。 当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。
为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的连接。 现在由你负责连接这些计算机,你的任务是使任意两台计算机都连通(不管是直接的或间接的)。

输入
第1行:1个整数n(2≤n≤100),表示计算机的数目。
此后的n行,每行n个整数。第x+1行y列的整数表示直接连接第x台计算机和第y台计算机的费用。

输出
第1行:1个整数,表示最小的连接费用

样例输入
3
0 1 2
1 0 1
2 1 0
样例输出
2

分析-得出结论-水题

输入的是一个邻接矩阵,要求任意两点连通+权值和最小。
嗯任意两点连通很明显是在暗示你往树方面想,权值和最小则是在疯狂暗示你 “最小生成树”。
于是我们得出结论:这是道模板型水题

Kruskal算法-以边选点

-简介

Kruskal的思想很简单:
你要最小和是吧?每次给你加条最小边
你要一棵树是吧?每次给你加边都无环
非常、非常经典的贪心思想

-实现

(1)将边按权值大小排序
(2)建立图集 T ,从小到大枚举每一条边
(3)如果将这条边加入 T 并不形成环,那么加入。
(4)直到所有的边都枚举一遍

-代码

struct Edge
{
    int u,v,value;
}G[MAXN*(MAXN-1)/2+5];
int F[MAXN+5];//并查集判断连通
int Tree[MAXN+5][MAXN+5];//邻接矩阵存树
bool cmp(Edge a,Edge b)
{
    if(a.value<b.value) return 1;
    else return 0;
}
int Find(int x)
{
    if(F[x]==0) return x;
    F[x]=Find(F[x]);
    return F[x];
}
void Union(int x,int y)
{
    F[Find(x)]=y;
    Find(x);Find(y);
}
int main()
{
    int n,len=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            Edge e;
            e.u=i;e.v=j;
            scanf("%d",&e.value);
            if(j>i) G[++len]=e;
        }
    sort(G+1,G+len+1,cmp);
    for(int i=1;i<=len;i++)
    {
        if(Find(G[i].u)!=Find(G[i].v))
        {
            Union(G[i].u,G[i].v);
            Tree[G[i].u][G[i].v]=Tree[G[i].v][G[i].u]=G[i].value;
        }
    }
}

-证明

注意!以下内容过于枯燥!请谨慎地往下读
用数学归纳法证明Kruskal算法适用于n阶图【阶就是结点的个数】:
(1): n=1 ,显然能够找到最小生成树。
(2):假设Kruskal算法对 kk 阶图适用,那么,在k+1阶图G中,我们把权值最小的边的两个端点a和b做一个合并操作,即把u与v合为一个点v’,把原来接在u和v的边都接到v’上去,这样就能够得到一个k阶图G’。
又:G’最小生成树T’可以用Kruskal算法得到。
所以我们现在只需证明 T+{<u,v>} 是G的最小生成树。

用反证法,如果 T+{<u,v>} 不是最小生成树,最小生成树是 T ,即 W(T)<W(T+{<u,v>})
T 一定包含 <u,v> ,否则,可以用 <u,v> 加入到T中,形成一个环【一定是一个环,如果是两个以上的环,则 u v 除了 <u,v> 还有至少两条路径,不满足树的特性】,删除环上原有的任意一条边,形成一棵更小权值的生成树。
然后: T{<u,v>} 是G’的生成树。所以 W(T{<u,v>})<=W(T) ,也就是 W(T)<=W(T)+W(<u,v>)=W(T+<u,v>) ,产生了矛盾。
于是假设不成立, T+{<u,v>} 是G的最小生成树,Kruskal算法对k+1阶图也适用。

由数学归纳法,Kruskal算法得证。

Prim算法-从点选边

-简介

不用判断连通性问题=没有并查集
任意点作为根,一步步选离得最近的点,直到把所有点收入囊中。
依然是贪心的思想

-实现

(1)选择任意点作为Root
(2)建立图集 T ,找到这样一条权值最小边 <u,v> 使得 uT vG,vT
(3)加入边 <u,v>
(4)直到 T 中有 N 个点

-代码

int G[105][105],Tree[105][105];
int Min[105],Pre[105];
...
Min[1]=INF;
for(int i=1;i<=n;i++)
    if(Min[i]!=INF)
    {
        Min[i]=G[1][i];
        pre[i]=1;
    }
for(int i=1;i<n;i++)
{
    int m=1;
    for(int j=1;j<=n;j++)
        if(Min[m]>Min[j]) m=j;
    Tree[m][pre[m]]=Tree[pre[m]][m]=Min[m];
    Min[m]=INF;
    for(int j=1;j<=n;j++)
        if(Min[j]!=INF&&G[j][m]<Min[j])
        {
            Min[j]=G[j][m];
            pre[j]=m;
        }
}

-证明

注意!以下内容过于……
好吧我知道你们会自觉跳过的
依然反证法:假设prim生成的不是最小生成树

1).设prim生成的树为 G
2).假设存在 G 使得 W(G)<W(G) , 则在 G 中存在 <u,v> 不属于 G
3).将 <u,v> 加入 G 中可得一个环,且 <u,v> 不是该环的最长边(因为 <u,v>G )(见上我们如何证明的Kruskal算法)
4).这与prim每次生成最短边矛盾
5).故假设不成立,命题得证.

简单易懂。

我知道你们在等什么-代码

自己改上面的Kruskal算法或Prim算法


第二题!-灌溉牧场-没钱修井,有钱接水管

题目描述

Farmer John 决定把水引到N个牧场 N (1 <= N <= 300),牧场正好从1..N编号。FJ可以用两种方法来引水:
(1)在牧场上挖一口水井;
(2)从其它已经有水的牧场连一根水管。
在牧场i挖水井的费用为W_i (1 <= W_i <= 100,000),在牧场i和j之间连水管的费用为P_ij (1 <= P_ij <=100,000; P_ij = P_ji; P_ii=0)
请算出FJ把水引到所有牧场的最小费用。

输入
第1行: 1个整数 N
第2..N + 1行: 只有1个整数 W_i
第N+2..2N+1行:每有 N 个空格分开的整数,第j个表示 P_ij

输出
第1行: 1个整数表示引水到所有牧场的最小费用。

样例输入
4
5
4
4
3
0 2 2 2
2 0 3 3
2 3 0 4
2 3 4 0
样例输出
9

样例说明
一共有4个牧场。挖水井的代价分别为5,4,4,3. 在不同牧场之间连水管的代价分别是 2, 3, 和 4 。Farmer John 在第4个牧场挖一口井,然后分别从第4个牧场连到其它牧场,代价为: 3 + 2 + 2 + 2 = 9.

分析-最小生成森林???

嗯……
假设我在A处建井,B处建井,共建两处
A,B管辖的范围肯定不能重叠,否则把A拆掉一样可以实现。
在A管辖的范围内,是一片最小生成树。
在B管辖的范围内,是一片最小生成树。
得出结论:题目要求将图 G 分成若干个不相交的最小生成树。
所以,这就是传说中的最小生成森林咯?

森林->一棵树

假设我们令森林内的所有树的根指向一个不存在的结点,则原本的森林就变成了一棵树
化归思想。
然后就可以对这棵树做我们想做的事了。

好理解的方法

井里的水从哪儿来?不妨令地底下有一口泉,下标为0。打井即是将泉与打井的位置相连,代价就为打井的代价。

代码的实现

#include<cstdio>
#include<algorithm>
using namespace std;
struct Edge
{
    int u,v,value;
}G[300*300+15];
int W[305],F[305];
int Min[305],Tree[305][305];
bool vis[305];
bool cmp(Edge a,Edge b)
{
    if(a.value<b.value) return 1;
    else return 0;
}
int Find(int x)
{
    if( F[x] == 0 ) return x;
    F[x]=Find(F[x]);
    return F[x];
}
void Union(int x,int y)
{
    F[Find(x)]=y;
    Find(x);Find(y);
}
int main()
{
    int n,len=0,Min=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&W[i]);
    for(int i=1;i<=n;i++)
    {
        Edge e;
        e.u=0;e.v=i;e.value=W[i];
        G[++len]=e;
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        {
            Edge e;
            e.u=i;e.v=j;
            scanf("%d",&e.value);
            if(j>i) G[++len]=e;
        }
    sort(G+1,G+len+1,cmp);
    int need=0,ans=0; 
    for(int i=1;i<=len;i++) 
    { 
        if(Find(G[i].u)!=Find(G[i].v)) 
        { 
            need++;
            ans+=G[i].value; 
            Union(G[i].u,G[i].v);
        } 
        if(need==n) break; 
    }
    printf("%d\n",ans);
}

EEEENNNNDDDDDDD!

就是这样,新的一天里,也请多多关照哦(ノω<。)ノ))☆.。~

猜你喜欢

转载自blog.csdn.net/tiw_air_op1721/article/details/78163331