[HNOI2006]公路修建问题(最小生成树/二分)

题意:传送门

题解:因为要满足至少做k条高速的,所以可以先将所有高速公路krusal一下,之后把没用到的高速路加到普通路上,因为害怕普通路比高速路花费还要更大,然后把剩下的高速路和普通路再次krusal一下,中间导导答案就可以了。

附上代码:

#include<bits/stdc++.h>
 
using namespace std;
 
const int maxn=1e4+5;
const int maxm=(2e4+5)*2;
 
struct edge{
    int u,v,w,next;
    bool used;
};
edge edges1[maxm],edges2[maxm];
int head1[maxn],tot1,head2[maxn],tot2;
 
bool cmp(edge a,edge b)
{
    return a.w<b.w;
}
 
int n,k,m,ans;
 
void init1()
{
    memset(head1,-1,sizeof(head1));
    tot1=0;
}
 
void init2()
{
    memset(head2,-1,sizeof(head2));
    tot2=0;
}
 
void add_edges1(int u,int v,int w)
{
    edges1[tot1].u=u;
    edges1[tot1].v=v;
    edges1[tot1].w=w;
    edges1[tot1].used=false;
    edges1[tot1].next=head1[u];
    head1[u]=tot1++;
}
 
void add_edges2(int u,int v,int w)
{
    edges2[tot2].u=u;
    edges2[tot2].v=v;
    edges2[tot2].w=w;
    edges2[tot2].next=head2[u];
    head2[u]=tot2++;
}
 
int f[maxn];
 
int find(int x)
{
    if(x==f[x]){
        return x;
    }else{
        return f[x]=find(f[x]);
    }
}
 
void merge(int a,int b)
{
    int x1=find(a),y1=find(b);
    if(x1!=y1){
        f[y1]=x1;
    }
}
 
void krusal1()
{
    for(int i=1;i<=n;i++){
        f[i]=i;
    }int cnt=0;
    for(int i=0;i<tot1;i++){
        edge &u=edges1[i];
        int a=find(u.u),b=find(u.v);
 
        if(a!=b){
            cnt++;
//            cout<<a<<" "<<b<<endl;
            u.used=true;
            merge(a,b);
            ans=max(ans,u.w);
        }
        if(cnt==k){
            return ;
        }
    }
}
 
void krusal2()
{
    for(int i=0;i<tot2;i++){
        edge &u=edges2[i];
        int a=find(u.u),b=find(u.v);
        int cnt=0;
        if(a!=b){
            cnt++;
            merge(a,b);
            ans=max(ans,u.w);
        }
    }
}
 
int main()
{
    init1();init2();
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<m;i++){
        int a,b,c1,c2;
        scanf("%d%d%d%d",&a,&b,&c1,&c2);
        add_edges1(a,b,c1);
        add_edges2(a,b,c2);
    }
    sort(edges1,edges1+tot1,cmp);
    krusal1();
    for(int i=0;i<tot1;i++){
        if(!edges1[i].used){
            edge &u=edges1[i];
            add_edges2(u.u,u.v,u.w);
        }
    }
    sort(edges2,edges2+tot2,cmp);
    krusal2();
    printf("%d\n",ans);
    return 0;
}

第二种做法:直接二分答案

附上代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
inline int read()
{
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x;
}
int n,K,m,ans;
int fa[10005];
struct data{int x,y,c1,c2;}e[20005];
int find(int x)
{return x==fa[x]?x:fa[x]=find(fa[x]);}
bool jud(int x)
{
	for(int i=1;i<=n;i++)fa[i]=i;
	int cnt=0;
	for(int i=1;i<=m;i++)
	{
		if(e[i].c1>x)continue;
		int p=find(e[i].x),q=find(e[i].y);
		if(p!=q)
		{fa[p]=q;cnt++;}
	}
	if(cnt<K)return 0;
	for(int i=1;i<=m;i++)
	{
		if(e[i].c2>x)continue;
		int p=find(e[i].x),q=find(e[i].y);
		if(p!=q)
		{fa[p]=q;cnt++;}
	}
	if(cnt!=n-1)return 0;
	return 1;
}
int main()
{
	n=read(),K=read(),m=read();
	for(int i=1;i<m;i++)
	    e[i].x=read(),e[i].y=read(),e[i].c1=read(),e[i].c2=read();
	int l=0,r=30001;
	while(r-l>1)
	{
		int mid=(l+r)>>1;
		if(jud(mid)){r=mid;}
		else {l=mid;}
	}
	printf("%d",r);
	return 0;
}

 

    原文作者:道路修建问题
    原文地址: https://blog.csdn.net/zhouzi2018/article/details/86743796
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞