【题解】小机关城

· · 个人记录

题面:

天明最近对机关城起了兴趣。 他用一些小物件搭起了一座机关城,当然,这只能算是玩具。 现在,他有n个机关,从1到n编号,他现在想让一些机关联通。 由于一些特殊原因,只有m对机关才可能建立联系。 他打算用绳子连接两个机关表示这两个机关能相互联系。 而连接一对机关所需要的绳子长度可能不同。 他笨手笨脚,所以为了防止绳子打结, 他希望使用的绳子中最长的绳子长度最短。 不过,他又有t个计划,对于每个计划,都有一个参数x, 表示这个计划至少需要x个机关相连。 你要告诉他满足计划条件的最长绳子的最短长度。

分析:

给定n个点m条边,一开始边是断开的,对于一个x,需要连接一些边,使得这个图中存在至少包含x个节点的联通块,求使用的边中,最长的那条边最短是多少。

首先,我们不知道那些边可以选,没有明确的目标

那我们就设选用的最长边不超过k

看看这个k,是否满足“图中存在至少包含x个节点的联通块”

当k恰好满足条件(即k-1无法满足条件),k就是答案“最长的那条边的最短值”

k的取值[1,1024],一一枚举耗时太长

可以思考

最长的边越长,就越容易满足“图中存在至少包含x个节点的联通块”,具有单调性,可以二分

还需要解决:对于一个k,判断其是否满足条件

最大的联通块包含的点>=x即满足条件

要找到最大的联通块,很明显用dfs

其他细节看注释

总结一下:二分+dfs

代码:

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

struct node {
    int to,v,nt;
} e[20005];
int n,m,t,p,head[10005],ie,num[10005];
int l,r,mid;
void add(int a,int b,int c) {//邻接表存图
    e[++ie].to=b,e[ie].v=c,e[ie].nt=head[a],head[a]=ie;
    return ;
}
int dfs(int x) {
    //num[x]表示x能到达的且没有走过的点数
    //第一次搜一个联通块里的点x,搜完后num[x]就是联通块的点数
    if(num[x]!=0) return 0;//num[x]=0表示没搜过,没搜过就搜,否则直接返回
    num[x]=1;//自己
    for(int i=head[x];i;i=e[i].nt)
        if(e[i].v<=mid)//mid即分析里的k,如果该边可选就搜
            num[x]+=dfs(e[i].to);//统计该点能到达的且没有走过的点
    return num[x];
}
int work(int v) {
    for(int i=1;i<=n;i++)//搜联通块
        if(dfs(i)>=v)
            return 1;//满足
    return 0;
}
int main() {
    scanf("%d %d %d",&n,&m,&t);
    for(int i=1,a,b,c;i<=m;i++) {
        scanf("%d %d %d",&a,&b,&c);
        add(a,b,c);
        add(b,a,c);
    }
    for(int i=1;i<=t;i++) {
        scanf("%d",&p);
        l=1,r=1025;
        while(l<r) {//二分
            memset(num,0,4*(n+1));
            mid=l+r>>1;
            if(work(p)) r=mid;
            else l=mid+1;
        }
        if(l==1025) printf("QAQ\n");//无解
        else printf("%d\n",l);
    }
    return 0;
}