【图论,树上路径倍增算法】NOIP2013货车运输

7 篇文章 0 订阅

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

输入
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
输出
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。

比赛时我姿势太少,直接写的暴力,滚粗了……

正解:
先根据原图找一个最大生成树。
每一次询问就是找 ab 的最近公共祖先。
由于数据大,就用树上路径倍增算法找最近公共祖先。

蒟蒻第一次学习树上路径倍增算法,若大神对以下解释有补充,欢迎留言,谢谢。O(∩_∩)O
先预处理:
用数组 f[i][j] 表示节点i的第 2j 个祖先,那么 f[i][0] 就是节点i的父亲,得到递推式: f[i][j]=f[[i][j1]][j1]
令数组 g[i][j] 表示节点i到第 2j 个祖先的最小边权,递推式: g[i][j]=min(g[i][j1],g[f[i][j1]][j1])

对每一次询问,找 ab 的最近公共祖先。
首先要统一 ab 的深度(当然是将深的那个节点上移,使得深度一致),之后使用倍增算法寻找最近公共祖先。

结果我在实现过程中把if(u==v)return ans;(在代码第90行)自作主张地删了,就WA- -

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define MAXM 50050
#define MAXN 10050
#define MAXD 22
#define min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int INF=2147483647;

int n ,m ;
int fa[MAXN] ;
int f[MAXN][MAXD] ,g[MAXN][MAXD] ,dep[MAXN] ;
bool vis[MAXN] ;

int getroot(int a)
{
    if(fa[a]==a)return a;
    return fa[a]=getroot(fa[a]);
}

struct Edge
{
    int u ,v ,w ;
    bool operator < (const Edge &a)const
    {return w>a.w ;}
}e[MAXM];

struct node
{
    int v ,w ;
    node *next ;
}edge[MAXN<<1] ,*adj[MAXN] ,*code=edge ;

void add(int a,int b,int c)
{
    node *p=++code ;
    p->v=b ,p->w=c ,p->next=adj[a] ;
    adj[a]=p ;
}

void kruskal()
{
    sort(e,e+m);
    int a ,b ;
    for(int i=0;i<m;++i)
    {
        a=getroot(e[i].u) ,b=getroot(e[i].v) ;
        if(a!=b)
        {
            fa[a]=b;
            add(e[i].u,e[i].v,e[i].w);
            add(e[i].v,e[i].u,e[i].w);
        }
    }
}

void dfs(int u)
{
    int v ;
    for(node *p=adj[u];p!=NULL;p=p->next)
        if(!vis[(v=p->v)])
        {
            vis[v]=1;
            f[v][0]=u ,g[v][0]=p->w ,dep[v]=dep[u]+1 ;
            dfs(v);
        }
}

int adjust(int &u,int high)
{
    int ans=INF;
    for(int i=MAXD-1;i>=0;--i)
        if(dep[f[u][i]]>=high)
        {
            ans=min(ans,g[u][i]);
            u=f[u][i];
        }
    return ans;
}

int solve(int u,int v)
{
    if(getroot(u)!=getroot(v)||u==v)return -1;
    int ans=INF;

    if(dep[u]!=dep[v])
        ans=dep[u]>dep[v]?adjust(u,dep[v]):adjust(v,dep[u]);
    if(u==v)return ans;
    for(int j=MAXD-1;j>=0;--j)
        if(f[u][j]!=f[v][j])
        {
            ans=min(ans,min(g[u][j],g[v][j]));
            u=f[u][j] ,v=f[v][j] ;
        }
    ans=min(ans,min(g[u][0],g[v][0]));
    return ans;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)fa[i]=i ;
    for(int i=0;i<m;++i)
        scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
    kruskal();

    for(int i=1;i<=n;++i)
        if(!vis[i])
        {
            vis[i]=1 ,dep[i]=0 ;
            dfs(i);
            g[i][0]=INF;
            f[i][0]=i;
        }
    for(int j=1;j<MAXD;++j)
        for(int i=1;i<=n;++i)
        {
            f[i][j]=f[f[i][j-1]][j-1] ;
            g[i][j]=min(g[i][j-1],g[f[i][j-1]][j-1]);
        }
    int T ,a ,b ;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",solve(a,b));
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值