洛谷1967 货车运输(最大生成树)(LCA)

版权声明:本文为博主原创文章,未经博主允许不得转载,除非先点了赞。 https://blog.csdn.net/A_Bright_CH/article/details/83175716

题目

A国有n n座城市,编号从 1 1到 nn,城市之间有 mm 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 qq 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

题解

最大生成树+lca求最小边权
有这么多条边,但每次都只会走那几条边权最大的边,其实,就是一棵最大生成树嘛。
把树找到后,设d[x][i]表示从x到f[x][i]中的最小边权,d[x][i]=min(d[x][i-1],d[f[x][i-1]][i-1])。
接下来的活儿交给lca。

总结

想要优化一个算法,不一定要用回这个算法。

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=10010,maxm=50010,maxq=30010;

int n,m;
struct E{int x,y,c,next;}a[maxm],e[maxn*2];int len=1,last[maxn];
bool cmp(E e1,E e2){return e1.c>e2.c;}//debug 最大生成树 
void ins(int x,int y,int c)
{
    e[++len]=(E){x,y,c,last[x]};last[x]=len;
}

int fa[maxn];
int findfa(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=findfa(fa[x]);
}
void kruskal()
{
    for(int i=1;i<=n;i++) fa[i]=i;
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++)//debug 遍历所有边(不一定满n-1条) 
    {
        int fx=findfa(a[i].x),fy=findfa(a[i].y);
        if(fx!=fy)
        {
            ins(a[i].x,a[i].y,a[i].c);
            ins(a[i].y,a[i].x,a[i].c);
            fa[fx]=fy;
        }
    }
}

int head,tail,q[maxn];
int dep[maxn],f[maxn][20],d[maxn][20];
void bfs(int S)
{
    head=0,tail=1;q[0]=S;
    dep[S]=1;
    for(int i=0;i<=16;i++) d[S][i]=inf;
    while(head<tail)
    {
        int x=q[head++];
        for(int k=last[x];k;k=e[k].next)
        {
            int y=e[k].y;
            if(dep[y]) continue;
            dep[y]=dep[x]+1;
            f[y][0]=x;d[y][0]=e[k].c;
            for(int i=1;i<=16;i++)
            {
                f[y][i]=f[f[y][i-1]][i-1];
                d[y][i]=min(d[y][i-1],d[f[y][i-1]][i-1]);
            }
            q[tail++]=y;
        }
    }
}
int lca(int x,int y)
{
    int re=inf;
    if(dep[x]<dep[y]) swap(x,y);
    for(int i=16;i>=0;i--)
        if(dep[f[x][i]]>=dep[y])
        {
            re=min(re,d[x][i]);
            x=f[x][i];
        }
    if(x==y) return re;
    for(int i=16;i>=0;i--)
        if(f[x][i]!=f[y][i])
        {
            re=min(re,min(d[x][i],d[y][i]));
            x=f[x][i],y=f[y][i];
        }
    if(f[x][0]==f[y][0]) return min(re,min(d[x][0],d[y][0]));//debug 别忘了与f[x/y][0]之间的边 
    return inf;//x和y没有公共祖先 
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].c);
    
    kruskal();
    for(int i=1;i<=n;i++)//因为不连通,所以每个点都要检查 
        if(!dep[i]) bfs(i);
    
    int Q;scanf("%d",&Q);
    while(Q--)
    {
        int x,y,ans;
        scanf("%d%d",&x,&y);
        if(findfa(x)!=findfa(y)){puts("-1");continue;}//用并查集判是否连通
        ans=lca(x,y);
        printf("%d\n",ans==inf?-1:ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/A_Bright_CH/article/details/83175716