hdu 2586 最近公共祖先

本文介绍了一种用于解决最近公共祖先问题的有效算法——倍增法。该算法通过预处理每个节点的2^k倍祖先,实现了在线查询两个节点的最近公共祖先,时间复杂度为O(logN)。文章提供了完整的C++代码实现。
题目链接

题意


n个村子,被n-1个条边连接,每次询问u和v的最近距离。


解析


  1. 求u和v的到他们的最近公共祖先的距离即可。
  2. 基本算法
    1. 在线算法 倍增法
      每次询问O(logN)
      d[i] 表示 i节点的深度, p[j,i] 表示 i 的 2^j 倍祖先
      那么就有一个递推式子 p[j,i]=p[p[j,i-1],i-1]
      这样子一个O(NlogN)的预处理求出每个节点的 2^k 的祖先
      然后对于每一个询问的点对a, b的最近公共祖先就是:
      先判断是否 d[a] > d[b] ,如果是的话就交换一下(保证 a 的深度小于 b 方便下面的操作)然后把b 调到与a 同深度, 同深度以后再把a, b 同时往上调(dec(j)) 调到有一个最小的j 满足p[j,a]!=p[j,b] (a b 是在不断更新的), 最后再把 a, b 往上调 (a=p[0,a], b=p[0,b]) 一个一个向上调直到a = b, 这时 a or b 就是他们的最近公共祖先

相关知识

http://baike.baidu.com/link?url=CxXG3rjGTlknKSJg17LrHjD5EVnoIrG_9GtS5ejbRp1GPv0FiGC-rB53pEcrGYkTnWUVQyvCCOu23KIVTDl_W_

代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
#include <cmath>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn = 40000+100;
typedef long long LL;
int parent[20][maxn], dis[maxn];
int dep[maxn];

vector<pair<int, int> >g[maxn];

void dfs(int u, int p, int d, int D) {
    parent[0][u] = p;
    dep[u] = d;
    for (int i=0; i<g[u].size(); i++) {
        if (g[u][i].first != p)
            dfs(g[u][i].first, u, d+1, D+g[u][i].second);
    }
    dis[u] = D;
}

void init(int V) {
    dfs(0, -1, 0, 0);
    for (int k=0; k+1 < 20; k++) {
        for (int v=0; v<V; v++) {
            if (parent[k][v] < 0)
                parent[k+1][v] = -1;
            else
                parent[k+1][v] = parent[k][parent[k][v]];
        }
    }
}

int lca(int u, int v) {
    if (dep[u] > dep[v])
        swap(u, v);
    for (int i=0; i<20; i++)
        if ((dep[v]-dep[u]) >> i & 1)
            v = parent[i][v];
    if (u == v)
        return u;
    for (int j=20-1; j>=0; j--) {
        if (parent[j][u] != parent[j][v]) {
            u = parent[j][u];
            v = parent[j][v];
        }
    }
    return parent[0][u];
}

int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n, m;
        memset(dis, 0, sizeof(dis));
        scanf("%d%d", &n, &m);
        for (int i=0; i<n-1; i++) {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            g[u-1].push_back(make_pair(v-1, w));
            g[v-1].push_back(make_pair(u-1, w));
        }
        init(n);
        for (int i=0; i<m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            int p = lca(u-1, v-1);
            printf("%d\n", dis[u-1]+dis[v-1]-2*dis[p]);
        }
    }
    return 0;
}
### HDU 1159 最长公共子序列 (LCS) 解题思路 #### 动态规划状态定义 对于两个字符串 `X` 和 `Y`,长度分别为 `n` 和 `m`。设 `dp[i][j]` 表示 `X[0...i-1]` 和 `Y[0...j-1]` 的最长公共子序列的长度。 当比较到第 `i` 个字符和第 `j` 个字符时: - 如果 `X[i-1]==Y[j-1]`,那么这两个字符可以加入之前的 LCS 中,则有 `dp[i][j]=dp[i-1][j-1]+1`[^3]。 - 否则,如果 `X[i-1]!=Y[j-1]`,那么需要考虑两种情况中的最大值:即舍弃 `X[i-1]` 或者舍弃 `Y[j-1]`,因此取两者较大者作为新的 LCS 长度,即 `dp[i][j]=max(dp[i-1][j], dp[i][j-1])`。 时间复杂度为 O(n*m),其中 n 是第一个字符串的长度而 m 是第二个字符串的长度。 #### 实现代码 以下是 Python 版本的具体实现方式: ```python def lcs_length(X, Y): # 初始化二维数组用于存储中间结果 m = len(X) n = len(Y) # 创建(m+1)x(n+1)大小的表格来保存子问题的结果 dp = [[0]*(n+1) for _ in range(m+1)] # 填充表项 for i in range(1, m+1): for j in range(1, n+1): if X[i-1] == Y[j-1]: dp[i][j] = dp[i-1][j-1] + 1 else: dp[i][j] = max(dp[i-1][j], dp[i][j-1]) return dp[m][n] # 测试数据输入部分可以根据具体题目调整 if __name__ == "__main__": while True: try: a = input().strip() b = input().strip() result = lcs_length(a,b) print(result) except EOFError: break ``` 此程序会读入多组测试案例直到遇到文件结束符(EOF)。每组案例由两行组成,分别代表要计算其 LCS 的两个字符串。最后输出的是它们之间最长公共子序列的长度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值