noip2013 货车运输

P1967 货车运输

    • 406通过
    • 1.4K提交
  • 题目提供者该用户不存在
  • 标签图论贪心2013NOIp提高组
  • 难度提高+/省选-

提交该题 讨论 题解 记录

最新讨论

题目描述

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

输入输出格式

输入格式:

 

输入文件名为 truck.in。

输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。意:x 不等于 y,两座城市之间可能有多条道路。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。

 

输出格式:

 

输出文件名为 truck.out。

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货

车不能到达目的地,输出-1。

 

输入输出样例

输入样例#1:
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
输出样例#1:
3
-1
3

说明

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000; 对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000; 对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

分析:这道题目就是求x到y的路径上最小值最大的权值,固定的方法就是最大生成树,当然如果是最大值最小,那么就是最小生成树.怎么求最大生成树呢?首先要是一棵树,两个点之间只有唯一的路径,怎么判断两个点是否连通呢?并查集!怎么要满足“最大”呢?排序!那么就先从大到小排序,把不在一个连通块的两个点连起来就可以了.那么就要求最小值最大,怎么求呢?可以知道这条唯一的路径是两个点到它们的lca的和,怎么求lca呢?倍增!首先一次dfs记录出f[i][j],g[i][j],即第i个节点的第2^j个祖先和所求的最小值最大的结果.同时记录下每个节点的深度,注意,这个深度是越大越往下.为了求lca(a,b),假设a的深度比b的深度大,那么把a往上移,直到深度相同,同时更新ans,这个时候如果a==b了,那么就返回,然后再让两个点同时上移,如果两个点上移之后相等了,因为我们是从大到小枚举2^j的,所以求出的点可能在lca上面,所以当两个祖先不是相同的才跳.那么跳到最后就跳到了lca的下一层,往上再跳一个就可以了.每次跳都要更新ans.

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 10010, maxm = 50010;

int n, m, q, fa[maxn], head[maxn], cnt, h[maxn], f[maxn][20], g[maxn][20], ans = 1000000000;
bool vis[maxn];

struct node
{
    int x, y, z;
}a[maxm];

struct node2
{
    int nextt, to, w;
}e[maxn * 2];

bool cmp(node a1, node b)
{
    return a1.z > b.z;
}

void dfs(int u, int depth)
{
    vis[u] = true;
    h[u] = depth;
    for (int i = head[u]; i; i = e[i].nextt)
        if (!vis[e[i].to])
        {
        f[e[i].to][0] = u;
        g[e[i].to][0] = e[i].w;
        dfs(e[i].to, depth + 1);
        }
    return;
}

int find(int x)
{
    if (fa[x] != x)
        fa[x] = find(fa[x]);
    return fa[x];
}

void add(int p, int q, int v)
{
    e[++cnt].to = q;
    e[cnt].nextt = head[p];
    head[p] = cnt;
    e[cnt].w = v;
}

void kruskal()
{
    sort(a + 1, a + 1 + m, cmp);
    for (int i = 1; i <= n; i++)
        fa[i] = i;
    for (int i = 1; i <= m; i++)
    {
        int fx = find(a[i].x), fy = find(a[i].y);
        if (fx == fy)
            continue;
        fa[fx] = fy;
        add(a[i].x, a[i].y, a[i].z);
        add(a[i].y, a[i].x, a[i].z);
    }
}

int lca(int a1, int b1)
{
    ans = 1000000000;
    if (a1 == b1)
        return 0;
    if (h[a1] < h[b1])
        swap(a1, b1);
    int k = int(log2(h[a1]));
    for (int i = k; i >= 0; i--)
        if (h[a1] - (1 << i) >= h[b1])
        {
        ans = min(ans, g[a1][i]);
        a1 = f[a1][i];
        }
    if (a1 == b1)
        return ans;
    for (int i = k; i >= 0; i--)
        if (f[a1][i] && f[a1][i] != f[b1][i])
        {
        ans = min(ans, min(g[a1][i], g[b1][i]));
        a1 = f[a1][i];
        b1 = f[b1][i];
        }
    ans = min(ans, min(g[a1][0], g[b1][0]));
    return ans;
}

int main()
{
    memset(vis, false, sizeof(vis));
    memset(head, 0, sizeof(head));
    memset(f, 0, sizeof(f));
    memset(g, 127, sizeof(g)); //INF
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++)
        scanf("%d%d%d", &a[i].x, &a[i].y, &a[i].z);
    kruskal();
    scanf("%d", &q);
    for (int i = 1; i <= n; i++)
        if (!vis[i])
        dfs(i, 1);
    for (int j = 1; (1 << j) <= n; j++)
        for (int i = 1; i <= n; i++)
            if (f[i][j - 1])
            {
        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]);
            }
    for (int i = 1; i <= q; i++)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        if (find(x) != find(y))
            printf("-1\n");
        else
            printf("%d\n", lca(x, y));
    }

    return 0;
}

 

 

posted @ 2016-08-06 21:31  zbtrs  阅读(1772)  评论(6编辑  收藏  举报