NOIP2013 货车运输(最大生成树+LCA)

模拟考试的时候暴搜,结果写丑了,分都不分

下来啃了一下题解,发现要用到一个叫做倍增的东西,还没有学过。但是老师说的,没有那个东西,写暴力也有30~40分。。。

我觉得最大生成树还是很好理解的,因为我们要求的是图中任意两个点之间的路径上,使得边权的最小值尽量大。因此首先求最大生成树。

当我们得到最大生成树后,要求两个点之间边权最小值,我们可以首先找到他们的公共祖先。这里有一篇写得很详细的代码,并且注明了各种写法的得分http://blog.csdn.net/gengmingrui/article/details/47414315

求出最近公共祖先的方法可以用倍增,这里有详细的介绍,我觉得写得很好http://blog.sina.com.cn/s/blog_963453200101k6up.html

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define MAXN 10005
using namespace std;
int n,m;
struct T
{
    int v;
    int w;
    int next;
}edge[100005];

struct P
{
    int u;
    int v;
    int w;
}a[MAXN*5];
bool cmp(P x,P y)
{
    return x.w > y.w;
}

int head[MAXN],cnt;
int f[MAXN];
int find(int x)//并查集,判断是否在同一个集合内
{
    if(f[x] == x) return f[x];
    else return f[x] = find(f[x]);
}
void Add_edge(int u,int v,int w)//树连边
{
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].next = head[u];
    head[u] = cnt++;
}
void Union(int u,int v)//联通块
{
    int x = find(u);
    int y = find(v);
    if(x != y) f[x] = y;
}
void kruskal()//最大生成树
{
    for(int i = 0; i <= MAXN; i++)
        f[i] = i;
    for(int i = 1; i <= m; i++)
    {
        int u = a[i].u,v = a[i].v;
        if(find(u) != find(v))
        {
            Union(u,v);
            Add_edge(u,v,a[i].w);
            Add_edge(v,u,a[i].w);
        }
    }
}

int up[MAXN][25],g[MAXN][25],h[MAXN];//up[i][j]表示i的第2^j个祖先,g[i][j]表示i到i的第2^j个祖先路径上的最小权值,h[i]表示i在树中深度
bool vis[MAXN];
void build_tree(int u)
{
    vis[u] = 1;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!vis[v])
        {
            g[v][0] = edge[i].w;
            up[v][0] = u;
            h[v] = h[u]+1;
            build_tree(v);
        }
    }
}
//把较深的一个点往上提,并记录他到祖先边权最小值,用他的一个祖先代替他
int move(int &u,int H)
{
    int res = 123546789;
    for(int i =20; i >= 0; i--)
    {
        if(h[up[u][i]] >= H)
        {
            res = min(res,g[u][i]);
            u = up[u][i];
        }
    }
    return res;
}
int query(int u,int v)//自认为是最难的地方
{
    if(find(u) != find(v)) return -1;
    int res = 123456789;
    if(h[u] != h[v]) res = h[u] > h[v]?move(u,h[v]):move(v,h[u]);
    if(u == v) return res;
    for(int i = 20; i >= 0; i--)//倍增的同时记录最小值,两个点越来越逼近公共祖先
    {
        if(up[u][i] != up[v][i])
        {
            res = min(res,min(g[u][i],g[v][i]));
            u = up[u][i];
            v = up[v][i];
        }
    }
    res = min(res,min(g[u][0],g[v][0]));//实际上到了这一步up[x][0] == up[y][0]因为它们的已经在同一棵子树里面
    //printf("up[u][0]: %d\n",up[u][0]);
    //printf("up[v][0]: %d\n",up[v][0]);
    return res;
}

int main()
{
    memset(head,-1,sizeof head);
    scanf("%d%d",&n,&m);
    for(int i = 1; i <= m; i++)
        scanf("%d%d%d",&a[i].u,&a[i].v,&a[i].w);
    sort(a+1,a+m+1,cmp);
    kruskal();

    for(int i = 1; i <= n; i++)//构建森林,并且初始化h,up,g
    {
        if(!vis[i])
        {
            h[i] = 0;
            build_tree(i);
            g[i][0] = 123456789;
            up[i][0] = i;
        }
    }
    for(int i = 1; i <= 20; i++)//预处理up和g,i大了也没有什么影响
    {
        for(int j = 1; j <= n; j++)
        {
            up[j][i] = up[up[j][i-1]][i-1];
            g[j][i] = min(g[j][i-1],g[up[j][i-1]][i-1]);
        }
    }

    int q;
    scanf("%d",&q);
    for(int i = 1; i <= q; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",query(x,y));
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-24 12:50:56

NOIP2013 货车运输(最大生成树+LCA)的相关文章

货车运输(最大生成树,LCA)

洛咕 题意:A国有n座城市,编号从1到n,城市之间有m条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有q辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物? 分析:显然限重就是边权.构建出图的最大生成树,然后树上每个节点dfs预处理出f[v][0]和dis[v][0],分别表示节点v的\(2^0\)级祖先(即父节点)是谁以及它到父节点的距离;然后对于每一个询问,利用倍增思想在树上LCA. #include<bits/stdc++.h> using nam

luogu1967[NOIP2013D1T3] 货车运输 (最大生成树+LCA)

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z

poj1330|bzoj3732|noip2013 货车运输 kruskal+倍增lca

学了一早上倍增,感觉lca还是tarjan好写. poj1330 1 #include <stdio.h> 2 #include <string.h> 3 #include <queue> 4 #include <algorithm> 5 #define DEG 20//2^20 6 #define maxn 10010 7 using namespace std; 8 struct node 9 { 10 int v, next; 11 }a[maxn*2

倍增LCA NOIP2013 货车运输

货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条

Codevs3278[NOIP2013]货车运输

3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入描述 Input Description 第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和

noip2013货车运输

P1967 货车运输 题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y

$Noip2013/Luogu1967$ 货车运输 最大生成树+倍增$lca$

$Luogu$ $Sol$ 首先当然是构建一棵最大生成树,然后对于一辆货车的起点和终点倍增跑$lca$更新答案就好.记得预处理倍增的时候不仅要处理走了$2^i$步后是那个点,还有这中间经过的路径权值的最小值以便之后统计答案. 再一看发现这题并没说给的图是联通的,也就是说跑了最大生成树之后可能有若干棵树.所以构树的时候要注意不能随便选一个点构完就不管了,要对每一个联通块都构一次.其他的地方似乎没有因为它有多棵树而有什么不同,只是询问的时候看下是不是一个联通块里的就好. $Code$ #includ

luogu P1967 货车运输 最大生成树 倍增LCA

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <cmath> 5 using namespace std; 6 struct edg 7 { 8 int x,y,w; 9 friend bool operator < (edg x,edg y) 10 { 11 return x.w > y.w; 12 } 13 } vec[110000]; 1

洛谷 P1967 货车运输 Label: 倍增LCA &amp;&amp; 最小瓶颈路

题目描述 A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物. 输入输出格式 输入格式: 输入文件名为 truck.in. 输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道 路. 接下来 m 行每行 3 个整数 x. y. z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z