问题描述
某景区一共有 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;
}
希望对你有帮助.