BZOJ1196: [HNOI2006]公路修建問題

題目

BZOJ1196: [HNOI2006]公路修建問題

題解

刷水題一直不好意思寫單篇題解,直到我看見了這道題。
把百度搜出來的題解看了前兩頁。全是二分答案,我纔不會告訴你這題,不用二分。當然我一開始也覺得是道二分水題。不過其實不二分也比較水,因爲二分能過所以大家沒有想太多,二分也是經典套路。

我寫到check的時候猶豫了。check在幹啥?把cost <=mid 的一級邊先加進去,然後加剩下的二級邊看能否連通,我怎麼覺得這每次做的動作差不多啊。於是誕生了不用二分的想法,若一級邊爲a邊,二級邊爲b邊,a,b先排序,然後先加k條a邊進圖,注意,這裏不能只加前k小的a邊,因爲有的a邊加不進去,要成功加入k次才行。加完之後,兩個pointer,分別表示a,b邊加到哪裏了,在不連通的時候,每次加代價比較小的邊,直到連通。其實你有沒有發現,先加k條a邊進圖之後,這就是個最小生成樹了,因爲限制條件沒了,妙。

代碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;

const int maxn=10000+10;
const int maxm=20000+10;

struct Edge{
    int u,v,c;Edge(int u=0,int v=0,int c=0):u(u),v(v),c(c){}
    inline bool operator < (const Edge &rhs)const{return c<rhs.c;}
}e1[maxm],e2[maxm];

int p[maxn],cnt;
int findset(int x){return p[x]==x?x:p[x]=findset(p[x]);}

inline void merge(int u,int v)
{
    u=findset(u);v=findset(v);
    if(u!=v) p[u]=v,--cnt;
}

int main()
{
    int n,k,m;cin>>n>>k>>m;cnt=n;--m;

    if(n==1) return puts("0"),0;

    for(int i=1,u,v,c1,c2;i<=m;++i)
    {
        scanf("%d%d%d%d",&u,&v,&c1,&c2);
        e1[i]=Edge(u,v,c1);e2[i]=Edge(u,v,c2);
    }
    sort(e1+1,e1+m+1);sort(e2+1,e2+m+1);
    for(int i=1;i<=n;++i) p[i]=i;

    int p1=1,p2=1;

    while(cnt != n-k) merge(e1[p1].u,e1[p1].v),++p1;
    while(cnt!=1)
    {
        if(p2==m+1 || e1[p1].c < e2[p2].c) merge(e1[p1].u,e1[p1].v),++p1;
        else merge(e2[p2].u,e2[p2].v),++p2;
    }
    cout<<max(e1[p1-1].c,e2[p2-1].c)<<endl;
    return 0;
}
發佈了167 篇原創文章 · 獲贊 76 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章