Acdream Path 动态规划
这一题不是自己想出来的思路,看了一眼解题报告。
题意是这样的,求给定的一棵树中是否存在一条长度为L的路径,注意这个值可能为负数,这也是简洁版的解题报告的最后一句忠告。这题有一个非常好的特性,那就是所有的边的长度要么为1,要么为2,也就是说我们所求得的路径是由若干个1,2组成的。那么也就有了下面的结论:当我们得到一条长度为S的路径时,现在考虑到构成路径S的左右两端的两条边,这两条边的组合情况是(1, 1), (1, 2), (2, 2)那么注意到我们始终可以去掉长度为2的一段,因此有结论:若整个树中最长的偶数边为EM,最长的奇数边为OM,由于任何一个询问不是奇数就是偶数,那么如果这个数是偶数的话,则判定其是否小于我们计算出来的EM,同理,奇数的话,则判定是否小于OM即可。
求解的过程就是从任意一个点开始进行dfs,并且对于每一条边保留两个值,表示从这个顶点的这一条边向下能够得到的偶数和奇数路径的最大长度,再结合其孩子的情况加之边的奇偶进行动态规划。
代码如下:
#include <iostream> #include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> using namespace std; struct Node { int x, fee, next; int path[2]; // 记录该条路径下的奇偶最长路径 }e[200010]; int head[100005], N, Q, idx, OM, EM; bool vis[200010]; void insert(int a, int b, int c) { ++idx; e[idx].x = b, e[idx].fee = c; e[idx].path[0] = e[idx].path[1] = 0; e[idx].next = head[a]; head[a] = idx; } void update(int x) { for (int i = head[x]; i != -1; i = e[i].next) { if (vis[i]) { continue; } vis[i] = 1; update(e[i].x); int oM = 0, eM = 0; for (int j = head[e[i].x]; j != -1; j = e[j].next) { if (e[j].x == x) continue; // 该条线路已被占用,否则形成了往返的线路 eM = max(eM, e[j].path[0]); // 子节点除本身路径之外其他路径的偶最长路 oM = max(oM, e[j].path[1]); } if (e[i].fee == 1) { // 如果这条路径为奇数 if (oM != 0) { e[i].path[0] = oM + 1; } e[i].path[1] = eM + 1; } else { e[i].path[0] = eM + 2; if (oM != 0) { e[i].path[1] = oM + 2; } } EM = max(EM, e[i].path[0]); OM = max(OM, e[i].path[1]); } } int main() { while (scanf("%d %d", &N, &Q) == 2) { OM = EM = 0; memset(head, 0xff, sizeof (head)); memset(vis, false, sizeof (vis)); int a, b, c; idx = -1; for (int i = 1; i < N; ++i) { scanf("%d %d %d", &a, &b, &c); insert(a, b, c); insert(b, a, c); } update(1); int x; for (int i = 0; i < Q; ++i) { scanf("%d", &x); if (x < 0) { puts("No"); continue; } if (x & 1) { puts(x <= OM ? "Yes" : "No"); } else { puts(x <= EM ? "Yes" : "No"); } } } return 0; }