三级排序

三级排序
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 363 Accepted: 177

Description

给你三维坐标(x, y, z),请给三维坐标排序,优先按x值从小到大排序,当x值一样时,按y值从小到大排序,当y值一样时,按z值从小到大排序。

Input

输入数据的第一行是一个数据T,表示有T组数据。
对于每组数据先输入n (1 < n < 50000),表示有n个三维坐标,然后是n行,每行输入三个整数x, y, z。

Output

对于每组输入数据,先输出n,然后输出n行,为排序后的坐标。

Sample Input

2
2
1 2 3
3 2 1
4
2 3 4
2 2 3
2 3 2
3 4 1

Sample Output

2
1 2 3
3 2 1
4
2 2 3
2 3 2
2 3 4
3 4 1

【题目链接】微笑点点点微笑

【题目分析】
做这个题时我是通过分类中STL来找的,如果只想做出来可以定义二维数组来 做会好懂点,这里就不详细说明了,都应该会,下面接着我用STL做的分析吧,这 到题我最初用的string对象作为map元素来做的,后来通过提交和验证,发现竟然 不行,空格的ASCII竟然比数字的ASCII大,所以有不相等位的时候就会错,之后就 不知道怎么做了,不过看了一下书,想起了用map键值两变量来做,做了一半想起 输入时如果相同的x和y坐标相等不就覆盖了,试试定义三个吧,想到可以用映照数 据++来做,输出时用一个while()就可以了,谁知道这都可以这样一做加深了我对 map和重载字符的了解,还是很有收获的大笑

【代码实现】
#include<string>
#include<map>
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
struct Info
{
    int ax;
    int by;
    int cz;
    bool operator < (const Info &a) const       // 重载<运算符
    {
        if(a.ax!=ax)            // 比较x坐标
            return a.ax>ax;
        else if(a.by!=by)       // 如果x相等比较y坐标
            return a.by>by;
        else                    // 如果前面都相等比较z坐标
            return a.cz>cz;
    }
};
int main()
{
    string s;
    map<Info,int>m;   // 定义属于自己的容器
    int t;
    cin>>t;     // 输人t组数据
    while(t--)
    {
        int n,i,a,b,c;
        cin>>n;
        Info info;      // 定义结构体变量
        getchar();
        map<Info,int>::iterator it;
        for(i=0;i<n;i++)
        {
            cin>>a>>b>>c;   // 输出坐标
            info.ax=a;
            info.by=b;
            info.cz=c;
            m[info]++;      // 插入到map容器中
        }
        cout<<n<<endl;
        for(it=m.begin();it!=m.end();it++)
        {
            while((*it).second!=0)      // 如果输入的坐标重复,就多次输出
            {
                cout<<((*it).first).ax<<" "<<((*it).first).by<<" "<<((*it).first).cz<<endl;
                (*it).second--;
            }

        }
        m.clear();
    }
    return 0;
}
自己探索着,做出来的,有什么不对,或有更简单的方法记得告诉我。

<think>我们之前已经讨论过,在比较函数中,如果只使用一个条件(比如x+y)并且当相等时返回true(即使用<=),这会破坏严格弱序,导致未定义行为。因此,我们需要在主要条件相等时,使用次要条件(如x)来比较,如果x也相等,则使用y来比较,这就是三级排序三级排序的目的: 1. 确保比较函数满足严格弱序(strict weak ordering)的要求,这是所有STL排序所必需的。 2. 当两个点的主要坐标变换(如x+y)相等时,我们需要一个确定的顺序,这样排序后相邻的点才是唯一的。 3. 特别重要的是,对于重复的点(即x和y都相等),我们需要让它们相邻,这样在生成边的时候,我们就能得到权值为0的边(因为曼哈顿距离为0),这条边对于最小生成树可能是必要的(如果两个重复点属于不同的连通分量,那么这条0权边可以连接它们而不增加总权值)。 在曼哈顿距离最小生成树算中,我们通过四个方向排序来生成候选边。每个方向排序后,我们只考虑相邻点之间的边。如果重复点没有被排在一起,那么它们之间的0权边可能不会被生成,从而导致错误(因为可能遗漏了必要的0权边)。 因此,三级排序是必要的,以确保: - 排序的稳定性(避免未定义行为) - 重复点相邻 - 生成所有必要的候选边(包括0权边) 下面我们用一个例子说明: 点A: (4,9), 点B: (4,9) -> 重复点 点C: (3,10) -> x+y=13 如果我们只按x+y排序,那么点A和点B的x+y都是13,它们相等。如果没有二级排序,那么排序时A和B的顺序可能是任意的,但如果我们有二级排序(先x,再y),那么A和B的x相等,y也相等,所以它们被视为相等(但排序时仍然需要确定一个顺序,所以我们会用三级排序来保证它们相邻,并且与其他点比较时顺序一致)。 实际上,在排序中,我们要求当两个元素相等时,比较函数应该返回false(即a不小于b,且b不小于a),这样它们就被视为相等。但是,如果我们使用<=,那么当相等时,a<=b为真,b<=a也为真,这就不满足严格弱序的要求(反对称性:如果a<=b且b<=a,则a等于b。但在比较函数中,我们要求如果a小于b为真,则b小于a必须为假;而相等时两者都不小于对方)。所以,我们使用三级排序,在主要条件相等时,继续比较次要条件,直到最后一级,这样就能保证任意两个元素都能确定顺序(除非完全相等,但即使完全相等,在排序中它们相邻即可,比较函数在完全相等时返回false(即a小于b为假,b小于a也为假))。 但是注意:在C++中,比较函数应该是一个严格弱序,它需要满足: - 非自反性:comp(a,a) 为假 - 反对称性:如果comp(a,b)为真,则comp(b,a)为假 - 传递性:如果comp(a,b)为真且comp(b,c)为真,则comp(a,c)为真 使用三级排序(即当主要条件相等时,继续比较次要条件)可以满足这些要求。 因此,在修正代码中,我们使用如下方式: if (主要条件不等) return 主要条件比较结果; else if (第二个条件不等) return 第二个条件比较结果; else return 第三个条件比较结果; 这样,对于任意两个不同的点,我们都能得到一个确定的顺序(即使它们的x+y相同,但x或y不同,我们也能排序);对于完全相同的点,比较函数会返回false(即a不小于b,且b也不小于a),这样在排序中它们相邻,但顺序未定(但没关系,因为我们要的是相邻,这样就会生成0权边)。 但是,在生成边的过程中,我们只取相邻的点对,所以重复点必须相邻才能生成0权边。 结论:三级排序是必要的,它保证了排序的正确性和生成边的完整性。 注意:题目允许任意输出顺序,所以边的顺序以及树的具体形态可以任意,但总权值必须正确。 所以,你的代码在增加了三级排序和修正输出格式后,应该能够通过题目。 但是,请注意,在输出边时,题目不要求字典序,所以我们可以按任意顺序输出边(只要是最小生成树的边)。但是,在生成树的过程中,我们收集的边是Kruskal算按权值从小到大加入的,在权值相同的情况下,边的顺序取决于排序后数组的顺序(即四个方向排序后相邻点对加入的顺序)。因此,我们不需要对最终输出的边进行字典序排序(除非题目要求,但本题不要求)。所以,在修正代码中,我们不需要对ans进行排序(即可以去掉最后对ans的sort),直接输出即可。 不过,在之前的修正代码中,我们为了确保输出边的顺序满足某种规范(比如字典序)而进行了排序。但题目允许任意顺序,所以这一步可以省略。但是,如果题目要求按字典序输出,那么就需要排序。根据题目描述:“如果有多解,你可以输出任意一种。”,所以我们可以省略对ans的排序。 但是,请注意:在Kruskal算中,当两条边权值相同时,我们加入的顺序是任意的,这可能会导致不同的生成树(但都是最小生成树)。所以,我们输出的边顺序可能不同,但权值和相同。 因此,最终代码中,我们可以不进行: sort(ans.begin(), ans.end()); 但是,注意:在生成边的时候,我们按四个方向分别生成,然后按权值排序,所以当权值相同时,边的顺序由v数组中的顺序决定(即四个方向排序后相邻点对加入的顺序)。这并不影响正确性。 所以,针对你的问题,三级排序是必要的,而输出边的顺序不需要字典序,所以可以任意。 下面我们修正一下:去掉对ans的排序,直接输出。 另外,注意:在输出边时,题目要求输出的是两个点的编号,我们不需要保证u<v,因为题目没有要求。但是,在输入样例中,输出是“5 2”这样的形式,并没有要求小节点在前。所以,我们也可以直接按照加入生成树的顺序输出(即v[i].u和v[i].v的顺序)。但是,注意在生成边的时候,我们加入的边的两个点id是排序后相邻点的id,这个id大小关系不确定。题目没有要求,所以任意顺序都可以。 因此,我们只需要修正比较函数和输出格式即可。 以下是去掉最终排序的代码(注意:只修改了输出部分,去掉了对ans的排序):</think>### 三级排序的必要性分析 在曼哈顿距离最小生成树算中,三级排序是确保算正确性的关键设计,主要原因如下: 1. **满足严格弱序要求**: C++标准库的`sort()`函数要求比较函数必须满足严格弱序(Strict Weak Ordering): ```cpp // 错误示例(违反严格弱序): bool cmp(int a, int b) { return a <= b; } // 当a==b时,cmp(a,b)和cmp(b,a)同时为true // 正确做三级排序): if (a.x + a.y != b.x + b.y) return a.x + a.y < b.x + b.y; if (a.x != b.x) return a.x < b.x; return a.y < b.y; ``` 三级排序确保任意两点都能确定唯一顺序,避免未定义行为。 2. **处理重复点问题**: - 当两点坐标完全相同时(如输入中的两个`(4,9)`),三级排序确保它们相邻 - 生成权值为0的边(`|4-4|+|9-9|=0`),这对连通重复点至关重要 - 避免重复点被分散导致漏边(0权边可能被遗漏) 3. **构建完整的候选边集**: ```plaintext A(1,2) -> x+y=3 B(2,1) -> x+y=3 C(1,1) -> x+y=2 // 无二级排序时可能排序为 [C, A, B] // 三级排序后保证顺序: // 1. 比较x+y: C(2) < A(3)=B(3) // 2. 比较x: A(1) < B(2) // 最终顺序: [C, A, B] ``` 确保相邻点间生成的边覆盖所有必要连接 4. **保证算正确性证明**: 曼哈顿距离最小生成树的理论证明要求: - 每个点只需连接特定方向最近邻的点 - 四个方向(45°间隔)覆盖所有可能的最短边 - 三级排序确保每个方向的"最近邻"定义明确唯一 ### 三级排序的工作机制 1. **第一级:主坐标变换**(决定基本方向) - `x+y`:东北/西南方向 - `x-y`:东南/西北方向 2. **第二级:x坐标**(处理主坐标相同的情况) ```cpp if (a.x != b.x) return a.x < b.x; ``` 3. **第三级:y坐标**(最终确定顺序) ```cpp return a.y < b.y; // 当x坐标也相同时 ``` ### 实际影响示例 输入点集:`(4,9), (4,9), (3,10)` ```plaintext // 无三级排序(仅x+y): (3,10): x+y=13 (4,9): x+y=13 // 顺序可能错乱 (4,9): x+y=13 // 有三级排序: (3,10): x+y=13, x=3 -> 第一 (4,9): x+y=13, x=4, y=9 -> 第二 (4,9): x+y=13, x=4, y=9 -> 第三 ``` 确保生成关键边:`(4,9)-(4,9)`(0权)和`(3,10)-(4,9)` ### 算实现要点 ```cpp // 正确比较函数实现模板 static inline bool p1(const node &a, const node &b) { // 第一级:主方向坐标 ll keyA = a.x + a.y; ll keyB = b.x + b.y; if (keyA != keyB) return keyA < keyB; // 第二级:x坐标 if (a.x != b.x) return a.x < b.x; // 第三级:y坐标 return a.y < b.y; } ``` ### 为什么不能省略任意一级? 1. **省略第三级**:当`x+y`和`x`都相同时,点可能乱序(如`(5,3)`和`(5,3)`) 2. **省略第二级**:`(3,5)`和`(4,4)`的`x+y`相同但未定义顺序 3. **使用`<=`替代**:违反严格弱序,导致排序崩溃(C++未定义行为) 三级排序是曼哈顿距离最小生成树算能处理任意输入(包括重复点、共线点等特殊情况)的基础保障。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值