1. 程式人生 > >洛谷 P2323 [HNOI2006]公路修建問題 解題報告

洛谷 P2323 [HNOI2006]公路修建問題 解題報告

noi inf node \n sort 描述 分享 直接 評測

P2323 [HNOI2006]公路修建問題

題目描述

技術分享圖片

輸入輸出格式

輸入格式:

技術分享圖片

技術分享圖片

在實際評測時,將只會有m-1行公路

輸出格式:

技術分享圖片


思路:
二分答案

然後把每條能加的大邊都加上,然後加小邊

但在洛谷的題解中,沒有采用二分答案而直接先處理k條大邊,再處理小邊的做法我認為是有問題的,歡迎證明或證偽。


Code:

#include <cstdio>
#include <algorithm>
const int N=10010;
const int M=20010;
struct node
{
    int u,v,w,id;
    bool friend operator <(node n1,node n2)
    {
        return n1.w<n2.w;
    }
}e1[M],e2[M];
int n,k,m;
void init()
{
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<m;i++)
    {
        scanf("%d%d%d%d",&e1[i].u,&e1[i].v,&e1[i].w,&e2[i].w);
        e2[i].u=e1[i].u;e2[i].v=e1[i].v;
        e1[i].id=e2[i].id=i;
    }
    std::sort(e1+1,e1+m);
    std::sort(e2+1,e2+m);
}
int f[N];
std::pair <int,int> ans[N];
int Find(int x){return f[x]=f[x]==x?x:Find(f[x]);}
void Merge(int x,int y){f[Find(y)]=Find(x);}
bool check(int len)
{
    for(int i=1;i<=n;i++) f[i]=i;
    int e=0,we=0;
    for(int i=1;i<m;i++)
    {
        if(e1[i].w>len) break;
        int u=e1[i].u,v=e1[i].v;
        if(Find(u)!=Find(v))
        {
            Merge(u,v);
            ans[++e].first=e1[i].id;
            ans[e].second=1;
            we++;
        }
    }
    for(int i=1;i<m;i++)
    {
        if(e2[i].w>len) break;
        int u=e2[i].u,v=e2[i].v;
        if(Find(u)!=Find(v))
        {
            Merge(u,v);
            ans[++e].first=e2[i].id;
            ans[e].second=2;
        }
    }
    return e==n-1&&we>=k;
}
void work()
{
    int l=1,r=30000;
    while(l<r)
    {
        int mid=l+r>>1;
        if(check(mid))
            r=mid;
        else
            l=mid+1;
    }
    check(l);
    printf("%d\n",l);
    std::sort(ans+1,ans+n);
    for(int i=1;i<n;i++)
        printf("%d %d\n",ans[i].first,ans[i].second);
}
int main()
{
    init();
    work();
    return 0;
}

2018.8.2

洛谷 P2323 [HNOI2006]公路修建問題 解題報告