蓝桥杯-3516-景区导游

问题描述

某景区一共有 N 个景点,编号 1到 N。景点之间共有 N -1条
双向的摆渡车线路相连,形成一棵树状结构。在景点之间往返只能
通过这些摆渡车进行,需要花费一定的时间。
小明是这个景区的资深导游,他每天都要按固定顺序带客人游览其
中 K 个景点:41,42,...,AK。今天由于时间原因,小明决定跳
过其中一个景点,只带游客按顺序游览其中 区-1个景点。具体
来说,如果小明选择跳过 4i,那么他会按顺序带游客游览
A1, A2,...,Ai-1,Ai+1,...,AK,(1 <i<K)。
请你对任意一个 Ai,计算如果跳过这个景点,小明需要花费多少时
间在景点之间的摆渡车上?
输入格式
第一行包含 2 个整数 N 和 K
以下 N -1行,每行包含 3 个整数 u,υ和t,代表景点u 和v之
间有摆渡车线路,花费t个单位时间。
最后一行包含 个整数 A1,42,...,AK,代表原定游览线路
输出格式
输出 区 个整数,其中第之个代表跳过 A;之后,花费在摆渡车上的
时间。

样例输入

6 4
1 2 1
1 3 1
3 4 2
3 5 2
4 6 3
2 6 5 1

样例输出

10 7 13 14

这题其实可以用暴力过一些的数据,思路也比较简单。

暴力思想:(DFS)

我们可以计算出旅游路线的总花费,再逐一删除每个结点,计算路线花费。虽然麻烦,但是很容易想出来。需要注意(删除某个旅游景点时,如果是第一个或最后一个,只需要减去该点到相邻结点的路径花费。
    删除中间结点时比较麻烦,例如删除6号景点时,需要减去2->6的路径花费,再减去6->5的路径花费,最后加上2->6的路径花费,超时的原因就是在这。)

代码如下:

#include<iostream>
#include<map>
#include<vector>
using namespace std;
typedef long long ll; //开long long防止爆int
typedef pair<int, int> PII; //用来表示起点和终点
vector<PII>edge[100005];// 用vector来存图  感觉要比链式前向星简单
map<PII, int>st; //st用来表示 起点到终点的距离
ll n, k;
bool vis[100005];//标记一下是否来过,后面好回溯
int path[100005];//用来存原定的游览路线
void dfs(int start, int end, int now, ll sum) { //start 表示起点,end表示终点,now表示当前在那个点,sum表示从起点到当前点的路径花费
	if (now == end) {
		st[{start, end}] = sum;
		st[{end, start}] = sum;
		return;
	}
	for (int i = 0; i < edge[now].size(); i++) {
		int next = edge[now][i].first;
		int waste = edge[now][i].second;
		if (!vis[next]) {
			vis[next] = 1;
			dfs(start, end, next, sum + waste);
			vis[next] = 0;
		}
	}
	return;
}
int main() {
	cin >> n >> k;
	for (int i = 1; i <= n - 1; i++) {
		int u, v, w;
		cin >> u >> v >> w;
		edge[u].push_back({ v,w });
		edge[v].push_back({ u,w });
	}
	for (int i = 1; i <= k; i++) {
		cin >> path[i];
	}
	int ans = 0;
	for (int i = 1; i <= k - 1; i++) {
		vis[path[i]] = 1;
		dfs(path[i], path[i + 1], path[i], 0);
		ans += st[{path[i], path[i + 1]}]; // ans为旅游路线的总花费
		vis[path[i]] = 0;
	}
	for (int i = 1; i <= k; i++) {
		int tmp = ans;
		if (i == 1) {
			tmp -= st[{path[i], path[i + 1]}];
		}
		else if (i == k) {
			tmp -= st[{path[i - 1], path[i]}];
		}
		else {
			tmp -= st[{path[i - 1], path[i]}];
			tmp -= st[{path[i], path[i + 1]}];
			vis[path[i - 1]] = 1;
			dfs(path[i - 1], path[i + 1], path[i - 1], 0);
			vis[path[i - 1]] = 0;
			tmp += st[{path[i - 1], path[i + 1]}];
		}
		cout << tmp << " ";
	}
	return 0;
}

正解需用LCA

代码如下:

#include <iostream>
#define max_num 1000005
typedef long long LL;
using namespace std;
struct edge {
    int to, next, time;
}e[max_num];
int tot, N, k, head[max_num], f[max_num][30], dep[max_num], num[max_num];
LL dis[max_num];
void add_edge(int u, int v, int time) { //这里用的是链式前向星来存图
    tot++;
    e[tot].to = v;
    e[tot].time = time;
    e[tot].next = head[u];
    head[u] = tot;
}
void dfs(int v, int father) {
    dep[v] = dep[father] + 1;
    for (int i = 1; (1 << i) <= dep[v]; i++) {
        f[v][i] = f[f[v][i - 1]][i - 1];
    }
    for (int i = head[v]; i; i = e[i].next) {
        int p1 = e[i].to;
        int p2 = e[i].time;
        if (p1 == father)
            continue;
        f[p1][0] = v;
        dis[p1] = dis[v] + p2;
        dfs(p1, v);
    }
}
LL lca(int x, int y) {    //一个LCA的模板 
    if (dep[x] < dep[y])
        swap(x, y);
    int time = 0;
    for (int i = 20; i >= 0; i--) {
        if (dep[f[x][i]] >= dep[y])
            x = f[x][i];
        if (x == y)
            return x;
    }
    for (int i = 20; i >= 0; i--) {
        if (f[x][i] != f[y][i]) {
            x = f[x][i];
            y = f[y][i];
        }
    }
    return f[x][0];
}
LL gettime(int u, int v, int lc) {   //计算两个结点到公共祖先的路径花费
    if (!u || !v)
        return 0;
    return dis[u] + dis[v] - 2 * dis[lc];
}
int main()
{
    cin >> N >> k;
    for (int i = 1; i < N; i++) {
        int u, v, w;
        cin >> u >> v >> w;
        add_edge(u, v, w);
        add_edge(v, u, w);
    }
    dfs(1, 0);
    for (int i = 1; i <= k; i++) {
        cin >> num[i];
    }
    LL sum = 0;
    for (int i = 1; i <= k; i++) {
        sum += gettime(num[i], num[i + 1], lca(num[i], num[i + 1]));
    }
    for (int i = 1; i <= k; i++) {
        if (i == 1) {
            LL ans = gettime(num[1], num[2], lca(num[1], num[2]));
            cout << sum - ans << " ";
        }
        else if (i == k) {
            LL ans = gettime(num[k - 1], num[k], lca(num[k - 1], num[k]));
            cout << sum - ans << " ";
        }
        else {
            LL ans = 0;
            ans += gettime(num[i - 1], num[i], lca(num[i - 1], num[i]));
            ans += gettime(num[i], num[i + 1], lca(num[i], num[i + 1]));
            ans -= gettime(num[i - 1], num[i + 1], lca(num[i - 1], num[i + 1]));
            cout << sum - ans << " ";
        }
    }
    return 0;
}

希望对你有帮助.

参考博客;[蓝桥杯]真题讲解:景区导游(DFS遍历、图的存储、树上前缀和与LCA)-CSDN博客

  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值