本文主要是介绍UVa12080 Pesky Heroes,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
题目链接
本题是2005年 ACM ICPC 欧洲区域赛 西北 赛区 的 E 题。
题意
参见CSDN博主「overload1997」的原创文章,这里贴一下他写的题意:
有一个有n个关键点的洞窟的路线图,其中1为入口。
保证只有一个点与1相连,且每个点最多只跟三个点有连边,以及从每个点到1的路有且仅有一条。
洞窟中还有m个陷阱,位于m个关键点中。
英雄从入口走到了某个死胡同且一路上没有触发到任何陷阱,之后英雄在这个死胡同里安营扎寨。
我们的目的是协同恶法师将英雄找出来,为此需要找遍所有的【从入口到终点一路上没有陷阱】的死胡同。
但是我们所在的宫殿离洞窟太远了,并没有办法从入口进去,所以只能通过传送门派小兵过去找。
恶法师可以从某条道路上开启一个传送门,并将小兵们从远方的宫殿送往这条路上。
小兵一开始的行动方向为背离入口的方向,每次遇到一个岔路口必定选择左边的路走,遇到死胡同就掉头。
你可以令小兵在遇到某个岔路口时选择右边的路而不是左边的路,不同的小兵可以选择不同的岔口但是每只小兵最多只能右转一次。
当小兵走回传送门的时候就会被传送回去。
小兵遇到陷阱会死掉,所以你必须令小兵避开陷阱。
问找遍所有符合条件的死胡同所需要开的最少的传送门数。
还有一点是通过枚举题意推出来的:小兵所走过的路中不能有出来送小兵来的传送门之外的其他传送门
以及还有一点需要注意的是,对于小兵来说的左边并不是我们看地图的视角的左边,而是小兵自己在洞窟里的视角的左边。
博主「overload1997」的这个题意分析基本能懂,但“对于小兵来说的左边并不是我们看地图的视角的左边,而是小兵自己在洞窟里的视角的左边”这句还是有点懵,我又从原创力文档上找到了雅礼中学女神大佬陈丹琦的题解:
两相结合,这一点我就理解了,这里我再补上一张图方便读者理解:
从父节点前进视角,若左转将走向左子节点,若右转将走向右子节点;
从左子节点回退视角,若左转将走向右子节点,若右转将走向父节点;
从右子节点回退视角,若左转将走向父节点,若右转将走向左子节点。
分析
理解完题意后,就知道用树上的动态规划求解了。
博主「overload1997」的动态规划使用的状态是d[N][3]:d[i][0]表示节点u的父节点和i的道路上无兵通过时,访问找遍子树i所有符合条件的死胡同所需要开的最少的传送门数;d[i][1/2]表示节点i的父节点和u的道路上有兵通过(但没有右转过/已经右转过),访问找遍子树i所有符合条件的死胡同所需要开的最少的传送门数。这样的状态转移需要分类讨论,不一一细述。
陈丹琦的动态规划状态是:用trap[i]表示子树i是否有陷阱(节点i本身是陷阱或者i的子树有陷阱则子树i有陷阱),f[i]表示访问完i这个子树最少需要安排多少传送门,g[i]表示已经为节点i和其父节点的道路上安排了传送门,遍历完整个子树i另外最少还需要多少个传送门。女神大佬的这个动态规划状态转移就简洁很多了:f[i] = min(f[left_child] + f[right_child], 1+g[i])。若trap[left_child] && trap[right_child],则g[i] = +inf;否则g[i] = min(g[left_child] + g[right_child], g[left_child] + f[right_child], g[right_child] + f[left_child])。
AC代码
博主「overload1997」的动态规划:
#include <iostream>
using namespace std;#define N 50010
#define M 505
int d[N][3], g[N][3], c[N], n, m; bool trap[N];void dfs(int u, int fa = 0) {if (trap[u]) {d[u][0] = 0; d[u][1] = d[u][2] = N;} else if (c[u] == 1 && u > 1) {d[u][0] = 1; d[u][1] = d[u][2] = 0;} else if (c[u] == 2 || u == 1) {int s = u==1 ? g[u][0] : g[u][0]+g[u][1]-fa;dfs(s, u);d[u][0] = d[s][0]; d[u][1] = d[s][1]; d[u][2] = d[s][2];} else {int l = g[u][0]!=fa ? g[u][0] : g[u][1], r = g[u][0]+g[u][1]+g[u][2]-fa-l;dfs(l, u); dfs(r, u);d[u][2] = d[l][2] + d[r][2];d[u][1] = min(d[l][2] + min(d[r][0], d[r][1]), d[r][2] + min(d[l][0], d[l][1]));d[u][0] = min(d[l][0] + d[r][0], 1 + d[u][1]);}
}int solve() {for (int u=1; u<=n; ++u) {cin >> c[u]; trap[u] = false;for (int i=0; i<c[u]; ++i) cin >> g[u][i];}while (m--) {int u; cin >> u; trap[u] = true;}dfs(1);return d[1][0];
}int main() {ios::sync_with_stdio(false);while (cin>>n>>m && n) cout << solve() << endl;return 0;
}
陈丹琦的动态规划:
#include <iostream>
using namespace std;#define N 50010
#define M 505
int t[N][3], f[N], g[N], c[N], n, m; bool trap[N];bool dfs(int u, int fa = 0) {if (trap[u]) {f[u] = g[u] = 0;} else if (c[u] == 1 && u > 1) {f[u] = 1; g[u] = 0;} else if (c[u] == 2 || u == 1) {int s = u==1 ? t[u][0] : t[u][0]+t[u][1]-fa;if (dfs(s, u)) trap[u] = true;f[u] = f[s]; g[u] = g[s];} else {int l = t[u][0]!=fa ? t[u][0] : t[u][1], r = t[u][0]+t[u][1]+t[u][2]-fa-l;if (dfs(l, u)) trap[u] = true;if (dfs(r, u)) trap[u] = true;g[u] = trap[l] && trap[r] ? N : min(g[l] + g[r], min(g[l] + f[r], g[r] + f[l]));f[u] = min(f[l]+f[r], g[u]+1);}return trap[u];
}int solve() {for (int u=1; u<=n; ++u) {cin >> c[u]; trap[u] = false;for (int i=0; i<c[u]; ++i) cin >> t[u][i];}while (m--) {int u; cin >> u; trap[u] = true;}dfs(1);return f[1];
}int main() {ios::sync_with_stdio(false);while (cin>>n>>m && n) cout << solve() << endl;return 0;
}
这篇关于UVa12080 Pesky Heroes的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!