pat顶级1013 Image Segmentation (35 point(s))

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/richenyunqi/article/details/87932143

欢迎访问我的pat顶级题解目录哦 https://blog.csdn.net/richenyunqi/article/details/86751676

题目描述

pat顶级1013 Image Segmentation (35 point(s))题目描述

算法设计

关于一个组件(component)的要求很多,但是关键还是这一条:对于任意的两个组件 C 1 , C 2 C1,C2 ,有 D ( C 1 , C 2 ) > H ( C 1 ) D ( C 1 , C 2 ) > H ( C 2 ) D(C1,C2)>H(C1)或者D(C1,C2)>H(C2) 其中, D ( C 1 , C 2 ) = D(C1,C2)=连接两个组件之间的最短边的长度 H ( C 1 ) = C 1 + c / C 1 H(C1)=C1中最小生成树最大边长度+常量c/C1中结点数目
我们可以通过Kruskal算法求解此题,只需对Kruskal算法进行一些修改。首先建立一个并查集数组,以一个并查集的树根作为一个组件的唯一标识。该并查集数组中的元素不再仅仅是指向其父节点的整数,而是一个新定义的结构体类型,其定义如下:

struct Node{
    int father;//父节点的编号,如果是根结点,表示整个组件结点个数的负值
    double maxWeight,h;//最小生成树中最长的边的长度、整个组件的H值
    Node(double f,int fa=-1,double w=0.0):h(f),father(fa),maxWeight(w) {}
};
vector<Node>nodes;//并查集数组

并查集中每棵树的根结点都唯一标识一个组件,注意,并查集中根结点的father值为整个组件结点个数的负值,识别根结点是通过查看其father值是否为负实现的。
接着按照Kruskal算法的操作,将所有的边存储到优先级队列中,然后依次弹出。注意合并两个并查集的条件不再仅仅是两个根结点不同,还要满足当前弹出的边的长度小于等于两个根结点的H值。此外,为了满足输出时从小到大排序的要求,还可以在合并两个并查集时,总是将编号大的根结点的father值置为编号小的根结点的编号。
读者可以参考我的C++代码进行理解。

注意点

常量c值、边的权重以及H值最好都用double类型存储。

C++代码

#include<bits/stdc++.h>
using namespace std;
struct Edge{//边类
    int v1,v2;
    double weight;
    Edge(int vv1,int vv2,double w):v1(vv1),v2(vv2),weight(w) {}
    bool operator <(const Edge&e)const{//重载小于运算符
        return this->weight>e.weight;
    }
};
priority_queue<Edge>edges;
struct Node{
    int father;//父节点的编号,如果是根结点,表示整棵树结点个数的负值
    double maxWeight,h;//最小生成树中最长的边的长度、整个组件的H值
    Node(double f,int fa=-1,double w=0.0):h(f),father(fa),maxWeight(w) {}
};
vector<Node>nodes;//并查集数组
int findFather(int x){//寻找根结点并进行路径压缩
    if(nodes[x].father<0)//father值小于0,该结点为根结点
        return x;//返回根结点编号
    int t=findFather(nodes[x].father);
    nodes[x].father=t;
    return t;
}
int n,m,a,b;
double c,w;
int main(){
    scanf("%d%d%lf",&n,&m,&c);
    while(n--)//初始化并查集
        nodes.push_back(Node(c));
    while(m--){
        scanf("%d%d%lf",&a,&b,&w);
        edges.push(Edge(a,b,w));
    }
    while(!edges.empty()){
        Edge e=edges.top();
        edges.pop();
        int ua=findFather(e.v1),ub=findFather(e.v2);
        if(ua>ub)//ua指向编号小的根结点,ub指向编号大的根结点
            swap(ua,ub);
        if(ua!=ub&&e.weight<=nodes[ua].h&&e.weight<=nodes[ub].h){//合并并查集
            nodes[ua].father+=nodes[ub].father;//更新ua指向的father值
            nodes[ub].father=ua;//让编号大的根结点向编号小的根结点合并
            nodes[ua].maxWeight=max(nodes[ua].maxWeight,e.weight);
            nodes[ua].h=nodes[ua].maxWeight+c/abs(nodes[ua].father);
        }
    }
    vector<set<int>>ans={{}};//存储最终结果
    for(int i=0;i<nodes.size();++i)//遍历并查集,找到各个根结点并放入ans中
        if(nodes[i].father<0){
            nodes[i].father=-ans.size();
            ans.push_back({i});
        }
    for(int i=0;i<nodes.size();++i)//遍历并查集,找到各个非根结点并放入其根节点在ans中的对应set中
        if(nodes[i].father>=0)
            ans[abs(nodes[findFather(i)].father)].insert(i);
    for(int i=1;i<ans.size();++i){//输出
        for(auto j=ans[i].begin();j!=ans[i].end();++j)
            printf("%s%d",j==ans[i].begin()?"":" ",*j);
        puts("");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/richenyunqi/article/details/87932143