P1034家族解题报告

   Name: P1034家族
  Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处
  Author: goal00001111
  Date: 24-12-08 12:05
  Description:
描述 Description     
     若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,现在给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。
规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。
输入格式 Input Format     
      第一行:三个整数n,m,p,(n<=5000,m<=5000,p<=5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。
以下m行:每行两个数Mi,Mj,1<=Mi,Mj<=N,表示Ai和Bi具有亲戚关系。
接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。
输出格式 Output Format     
      P行,每行一个'Yes'或'No'。表示第i个询问的答案为"具有"或"不具有"亲戚关系。
样例输入 Sample Input     
    6 5 3
    1 2
    1 5
    3 4
    5 2
    1 3
    1 4
    2 3
    5 6
样例输出 Sample Output     
      Yes
    Yes
    No
    
题目分析:
    本题是并查集的典型应用。
    由于数据量较大,所以我们应该使用并查集中的压缩路径和按大小求并高效算法,否则会超时。
    
说明:
算法思想:迭代和递归。
数据结构:并查集。
时间复杂度:O(M*logN),其中M表示M棵等价类树,N表示树的深度;
空间复杂度:O(MAX),MAX表示集合成员数量;
程序语言:分别用c++和pascal实现。
附注:关于并查集的详细内容请参考拙作《一种简单而有趣的数据结构--并查集》:
http://blog.youkuaiyun.com/goal00001111/archive/2008/12/24/3595844.aspx

c++代码:

#include<iostream>

using namespace std;

int FindFatherAndReducePath(int father[], int pos);
bool UnionBySize(int father[], int posI, int posJ);
bool SameFamily(int father[], int posI, int posJ);

int main()
{
    int posI, posJ;
    int n, m, p;
    
    cin >> n >> m >> p;
    
    int *father = new int[n+1];
    
    for (int i=0; i<=n; i++) //所有的数组元素值均初始化为-1
        father[i] = -1;
    
    for (int i=0; i<m; i++) //等价类划分
    {
        cin >> posI >> posJ;
        UnionBySize(father, posI, posJ);
    }
    
    for (int i=0; i<p; i++) //询问posI和posJ是否具有亲戚关系
    {
        cin >> posI >> posJ;
        if (SameFamily(father, posI, posJ))
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    
    delete []father;
    system("pause");
    return 0;
}

/*
函数名称:FindFatherAndReducePath
函数功能:查找族长并压缩路径:找到族长后,将所途经的前辈结点均指向族长
输入变量:int father[]:存储了家族树信息的整型数组
          int pos : 家族成员pos的序号(即数组元素下标)
输出变量:无
返回值:int :序号pos的族长大人的序号。若pos本身是族长,则返回自身
*/
int FindFatherAndReducePath(int father[], int pos)
{
    if (father[pos] < 0)
        return pos;
    //若自己不是族长,则找到族长后,将所途经的结点均指向族长    
    return father[pos] = FindFatherAndReducePath(father, father[pos]);
}

/*
函数名称:UnionBySize
函数功能:按大小求并:将成员posI和posJ合并到同一个家族
输入变量:int father[]:存储了家族树信息的整型数组
          int posI, posJ : 家族成员posI和posJ的序号(即数组元素下标)
输出变量:无
返回值:bool :合并是否成功信息。如果posI和posJ是同一个族长门下,不必合并,返回false,否则合并成功返回true
*/
bool UnionBySize(int father[], int posI, int posJ)
{
    //首先各自去寻找自己的族长
    int fI = FindFatherAndReducePath(father, posI);
    int fJ = FindFatherAndReducePath(father, posJ);
    
    if (fI == fJ) //如果是同一个族长门下,不必合并,即合并失败
        return false;
    else if (father[fI] < father[fJ])
    {//如果族长fI的实力比fJ强,即|fI|>|fJ|,则fI当族长,并修改father[fI]和father[fJ]
        father[fI] += father[fJ];
        father[fJ] = fI;
    }
    else              //否则fJ当族长
    {
        father[fJ] += father[fI];
        father[fI] = fJ;
    }
    
    return true;
}

/*
函数名称:SameFamily
函数功能:判断成员posI和posJ是否属于同一家族
输入变量:int father[]:存储了家族树信息的整型数组
          int posI, posJ : 家族成员posI和posJ的序号(即数组元素下标)
输出变量:无
返回值:若成员posI和posJ属于同一家族,返回ture,否则返回false
*/
bool SameFamily(int father[], int posI, int posJ)
{
    return FindFatherAndReducePath(father, posI) ==
           FindFatherAndReducePath(father, posJ);
}

PASCAL代码:

PROGRAM P1034 (INPUT, OUTPUT);
VAR
    father : ARRAY [1..5000] OF INTEGER;
    n, m, p, posI, posJ, i : integer;

FUNCTION FindFatherAndReducePath(pos : integer): integer;
    begin
        if father[pos] < 0 then
            FindFatherAndReducePath := pos
        {若自己不是族长,则找到族长后,将所途经的结点均指向族长}
        else
        begin
            father[pos] := FindFatherAndReducePath(father[pos]);
            FindFatherAndReducePath := father[pos];
        end; {else}
    end; {FindFatherAndReducePath}

PROCEDURE UnionBySize(posI, posJ : integer);
    var
        fI, fJ : integer;
    begin
        {首先各自去寻找自己的族长}
        fI := FindFatherAndReducePath(posI);
        fJ := FindFatherAndReducePath(posJ);
        
        if fI = fJ then {什么也不做}
        else if father[fI] < father[fJ] then
        begin {如果族长fI的实力比fJ强,即|fI|>|fJ|,则fI当族长,并修改father[fI]和father[fJ]}
            father[fI] := father[fI] + father[fJ];
            father[fJ] := fI;
        end {if}
        else {否则fJ当族长}
        begin
            father[fJ] := father[fI] + father[fJ];
            father[fI] := fJ;
        end; {else if}

    end; {UnionBySize}

FUNCTION SameFamily(posI, posJ : integer): boolean;
    begin
        SameFamily := (FindFatherAndReducePath(posI) = FindFatherAndReducePath(posJ));
    end; {SameFamily}

BEGIN {MAIN}
    read(n);
    read(m);
    read(p);

    for i:=1 to n do {所有的数组元素值均初始化为-1}
        father[i] := -1;

    for i:=1 to m do {等价类划分}
    begin
        read(posI);
        read(posJ);
        UnionBySize(posI, posJ);
    end; {for}

    for i:=1 to p do {询问posI和posJ是否具有亲戚关系}
    begin
        read(posI);
        read(posJ);
        if SameFamily(posI, posJ) then
            writeln('Yes')
        else
            writeln('No');
    end; {for}
END.

<think>嗯,用户问的是广义普氏算法和最近邻迭代算法的区别。我需要先理解这两个算法各自是什么,然后找出它们的区别。 首先,广义普氏算法,可能是指广义的Procrustes分析,用来对齐形状,比如在图像处理或统计形状分析中。普氏分析主要是通过缩放、旋转和平移来最小化点集之间的差异,属于无监督对齐方法。参考里提到的普氏分析法是迭代找标准形状,用最小二乘法优化仿射变换参数[^2][^3]。这可能涉及到多组点集对齐到一个平均形状上,每次迭代调整参数,直到收敛。 然后是最近邻迭代算法(Iterative Closest Point, ICP)。ICP主要用于三维点云配准,分为两步:最近邻匹配和变换估计。通过不断寻找最近点对应,然后计算最优刚体变换,直到误差最小。这个过程是迭代的,每次找到对应点再优化变换参数。 接下来要比较它们的区别。应用场景方面,广义普氏可能更多用于形状统计或生物学中对齐样本,而ICP用于点云配准,比如三维重建。数学原理上,普氏分析可能用最小二乘法优化缩放、旋转和平移参数,而ICP每次迭代先找对应点再优化刚体变换。对应关系处理方面,普氏不需要显式对应,假设点顺序已知;ICP需要动态建立对应关系,通过最近邻搜索。收敛方式可能也有差异,普氏通过多组对齐到平均形状,而ICP是逐步逼近最优变换。 还要注意用户提到的参考资料,引用2和3提到了普氏分析的最小二乘和参数优化,而引用1是关于粒子群优化的,可能不相关。因此需要集中参考2和3的信息,确保回答准确。 可能需要详细说明每个算法的步骤,然后对比它们的处理方式、数学优化目标、对应关系建立等。最后总结区别,帮助用户明确两者的不同应用和原理。</think>### 广义普氏算法 vs 最近邻迭代算法(ICP)区别详解 #### 1. **核心目标与定义** - **广义普氏算法(Generalized Procrustes Analysis, GPA)** 旨在通过**仿射变换**(缩放、旋转、平移)将多组形状对齐到统一坐标系,最小化所有形状与平均形状的差异。其数学表达式为: $$\arg\min_{s, R, T} \| s R p^T + T - q^T \|_F$$ 其中$s$为缩放因子,$R$为旋转矩阵,$T$为平移向量,$p$和$q$为待对齐的点集。 - **最近邻迭代算法(Iterative Closest Point, ICP)** 主要用于**刚体配准**(仅旋转、平移),通过迭代寻找最近邻点对应关系并优化变换参数,最终最小化两点集的距离。其目标函数为: $$\arg\min_{R, T} \sum_{i=1}^n \| R p_i + T - q_j \|^2$$ 其中$q_j$是$p_i$的最近邻点。 --- #### 2. **关键区别** | **维度** | **广义普氏算法** | **最近邻迭代算法(ICP)** | |-------------------|-----------------------------------------------|--------------------------------------------| | **应用场景** | 多形状对齐(如生物形态分析、统计形状建模) | 单对点云配准(如三维重建、机器人定位) | | **数学优化目标** | 最小化所有形状与平均形状的仿射差异(含缩放) | 最小化两点集刚体变换后的欧氏距离 | | **对应关系建立** | 假设点集间已知对应顺序(无需动态匹配) | 依赖动态最近邻搜索建立对应关系(可能引入误差) | | **迭代内容** | 迭代更新平均形状与仿射参数 | 交替优化对应关系与刚体变换参数 | | **参数自由度** | 包含缩放因子(更灵活) | 仅旋转和平移(更严格) | --- #### 3. **流程对比** **广义普氏算法流程** 1. 计算所有形状的初始平均形状 2. 对每个形状独立优化仿射参数($s, R, T$) 3. 更新平均形状 4. 重复步骤2-3至收敛。 **ICP算法流程** 1. 初始化粗略对齐 2. 对每个点寻找目标点集中的最近邻 3. 计算最优刚体变换($R, T$) 4. 应用变换并更新误差 5. 重复步骤2-4至收敛。 --- #### 4. **优缺点对比** - **广义普氏算法** - ✅ 支持多组对齐,适用于形状统计分析 - ✅ 包含缩放因子,适应不同尺度 - ❌ 依赖点集间的已知对应关系 - **ICP算法** - ✅ 动态建立对应关系,适应部分重叠点云 - ✅ 计算效率高,适合实时应用 - ❌ 对初始位置敏感,可能陷入局部最优 --- #### 5. **典型应用场景** - **广义普氏算法**:人脸关键点对齐、生物骨骼形态比较 - **ICP算法**:三维扫描模型拼接、自动驾驶中的点云匹配 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值