洛谷 題解 UVA1395 【苗條的生成樹 Slim Span】

【題意】

給出一個\(n(n<=100)\)個節點的的圖,求最大邊減最小邊儘可能小的生成樹。c++

【算法】

\(Kruskal\)算法

【分析】

首先把邊按邊權從小到大進行排序。對於一個連續的邊集區間\([L,R]\),若是這些邊使得\(n\)個點所有聯通,則必定存在一個苗條度不超過\(W[R]-W[L]\)的生成樹(其中\(W[i]\)表示排序後第\(i\)條邊的權值)。spa

從小到大枚舉\(L\),對於每一個\(L\),從小到大枚舉\(R\),同時用並查集將新進入\([L,R]\)的邊兩端的點合併成一個集合,與\(Kruskal\)算法同樣。當全部的點都聯通是中止枚舉\(R\),換下一個\(L\)(而且把\(R\)重置爲\(L\)),繼續枚舉。code

【代碼】

#include<bits/stdc++.h>
using namespace std;
const int MAXN=100+10;
const int MAXM=10000+10;
int n,m;
int fa[MAXN];
int maxn,ans=0x3f3f3f3f;
struct Node
{
    int u,v,w;
}edge[MAXM];
inline int read()
{
    int tot=0;
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    while(c>='0'&&c<='9')
    {
        tot=tot*10+c-'0';
        c=getchar(); 
    }
    return tot;
}
inline bool cmp(Node x,Node y)
{
    return x.w<y.w;
}
inline int find(int k)//並查集
{
    if(fa[k]==k)return k;
    else return fa[k]=find(fa[k]);
}
inline bool kruskal(int k)//判斷是否能造成生成樹
{
    maxn=0;
    int tot=0;
    for(int i=1;i<=n;i++)
        fa[i]=i;
    for(int i=k;i<=m;i++)
    {
        if(fa[find(edge[i].u)]!=fa[find(edge[i].v)])
        {
            maxn=edge[i].w;
            fa[find(edge[i].u)]=fa[find(edge[i].v)];
            tot++;
        }
        if(tot==n-1)return 1;//若是全部點都聯通,則返回true
    }
    return 0;//不然返回false
}
int main()
{
    while(1)
    {
        ans=0x3f3f3f3f;
        n=read();m=read();
        if(!n&&!m)break;
        for(int i=1;i<=m;i++)
        {
            edge[i].u=read();
            edge[i].v=read();
            edge[i].w=read();
        }
        sort(edge+1,edge+1+m,cmp);//給邊進行從小到大排序
        for(int i=1;i<=m;i++)//枚舉L
        {
            if(kruskal(i))
            {
                ans=min(ans,maxn-edge[i].w);//更新最小值
            }
        }
        if(ans!=0x3f3f3f3f)cout<<ans<<endl;
        else cout<<-1<<endl;//特判
    }
    return 0;
}

劉汝佳大法好!排序

相關文章
相關標籤/搜索