洛谷 P1967 货车运输 - 生成树、LCA

洛谷 P1967 货车运输

题目链接:洛谷 P1967 货车运输

算法标签: 图论生成树深度优先搜索(DFS)

题目

题目描述

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。

输入输出样例

输入 #1

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出 #1

3
-1
3

题解:

生成树(最大)+ DFS + 倍增LCA

整体思路就是在最大生成树上找一条最小边,实现的过程就是在最大生成树上跑倍增LCA。

那么既然这么说,我们就要单独把最大生成树建好(除去多余边),所以我们就需要在Kruskal的时候直接建树,具体代码实现:

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

在建好最大生成树之后,我们要DFS做几个处理:

1.处理出每个点的深度(LCA)

2.处理好每个点的上1个点(LCA)

3.处理好每个点到它的上一个点的距离(LCA边权)

代码实现如下:

void dfs(int x)
{
	for (int i = head[x]; i; i = nex[i])
	{
		int y = to[i];
		if (deep[y])
			continue ;
		deep[y] = deep[x] + 1;
		f[y][0] = x;
		w[y][0] = val[i];
		dfs(y);
	}
}

最终求答案就是一个倍增LCA的板子,要注意在每一步向上倍增的时候都要记录答案(最小边边权)

ans = min(ans, min(w[x][i], w[y][i]));

最终的ans就是对于这一对询问的答案。

LCA部分代码如下:

int lca(int x, int y)
{
	if (find(x) != find(y))
		return -1;
	int ans = inf;
	if (deep[x] > deep[y])
		swap(x, y);
	for (int i = 20; i >= 0; i -- )
	{
		if (deep[f[y][i]] >= deep[x])
		{
			ans = min(ans, w[y][i]);
			y = f[y][i];
		}
	}
	if (x == y) return ans;
	for (int i = 20; i >= 0; i -- )
	{
		if (f[x][i] != f[y][i])
		{
			ans = min(ans, min(w[x][i], w[y][i]));
			y = f[y][i];
			x = f[x][i];
		}
	}
	ans = min(ans, min(w[x][0], w[y][0]));
	return ans;
}

AC代码

#include <bits/stdc++.h>

using namespace std;

const int inf = 0x3f3f3f3f;

const int MAXN = 10010;
const int MAXM = 50050;

int n, m, q;

int fa[MAXN], deep[MAXN], f[MAXN][21], w[MAXN][21];

int tot, to[MAXM], val[MAXM], nex[MAXM], head[MAXN];

struct Edge{
	int x, y, val;
} edge[MAXM];

bool cmp(Edge a, Edge b)
{
	return a.val > b.val;
}

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

void add(int x, int y, int z)
{
	to[ ++ tot] = y;
	val[tot] = z;
	nex[tot] = head[x];
	head[x] = tot;
}

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

void dfs(int x)
{
	for (int i = head[x]; i; i = nex[i])
	{
		int y = to[i];
		if (deep[y])
			continue ;
		deep[y] = deep[x] + 1;
		f[y][0] = x;
		w[y][0] = val[i];
		dfs(y);
	}
}

int lca(int x, int y)
{
	if (find(x) != find(y))
		return -1;
	int ans = inf;
	if (deep[x] > deep[y])
		swap(x, y);
	for (int i = 20; i >= 0; i -- )
	{
		if (deep[f[y][i]] >= deep[x])
		{
			ans = min(ans, w[y][i]);
			y = f[y][i];
		}
	}
	if (x == y) return ans;
	for (int i = 20; i >= 0; i -- )
	{
		if (f[x][i] != f[y][i])
		{
			ans = min(ans, min(w[x][i], w[y][i]));
			y = f[y][i];
			x = f[x][i];
		}
	}
	ans = min(ans, min(w[x][0], w[y][0]));
	return ans;
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; i ++ )
	{
		scanf("%d%d%d", &edge[i].x, &edge[i].y, &edge[i].val);
	}
	kruskal();
	scanf("%d", &q);
	for (int i = 1; i <= n; i ++ )
	{
		if (!deep[i])
		{
			deep[i] = 1;
			dfs(i);
			f[i][0] = i;
			w[i][0] = inf;
		}
	}
	for (int i = 1; i <= 20; i ++ )
	{
		for (int j = 1; j <= n; j ++ )
		{
			f[j][i] = f[f[j][i - 1]][i - 1];
			w[j][i] = min(w[j][i - 1],w[f[j][i - 1]][i - 1]);
		}
	}
	for (int i = 1; i <= q; i ++ )
	{
		int x, y;
		scanf("%d%d", &x, &y);
		printf("%d\n", lca(x, y));
	}
	return 0;
}

posted @ 2019-11-12 14:57  筱柒_Littleseven  阅读(109)  评论(0编辑  收藏  举报