【BIT2021程设】14.水晶球

写在前面:

本系列博客仅作为本人十一假期过于无聊的产物,对小学期的程序设计作业进行一个总结式的回顾,如果将来有BIT的学弟学妹们在百度搜思路时翻到了这一条博客,也希望它能对你产生一点帮助(当然,依经验来看,每年的题目也会有些许的不同,所以不能保证每一题都覆盖到,还请见谅)。

不过本人由于学艺不精,代码定有许多不足之处,欢迎各位一同来探讨。

同时请未来浏览这条博客的学弟学妹们注意,对于我给出完整代码的这些题,仅作帮助大家理解思路所用(当然,因为懒,所以大部分题我都只给一个伪代码)。Anyway,请勿直接复制黏贴代码,小学期的作业也是要查重的,一旦被查到代码重复会严厉扣分,最好的方法是浏览一遍代码并且掌握相关的要领后自己手打一遍,同时也要做好总结和回顾的工作,这样才能高效地提升自己的代码水平。

加油!


成绩10开启时间2021年08月31日 星期二 12:30
折扣0.8折扣时间2021年09月7日 星期二 23:00
允许迟交关闭时间2021年10月10日 星期日 23:00

Description

和许多同龄女孩子一样,久莲也喜欢水晶球。

还有10

天,就是心心念念的他生日了。

久莲希望把全世界最大最好看的水晶球送给他。

她找到了宝石收藏家亚瑟斯,希望能够寻求他的帮助。

亚瑟斯很快被打动了,拿出了精心收集的n块美丽的水晶石,这些水晶石初始是长宽高分别为a,b,c的长方体。亚瑟斯许诺久莲可以从中取走1

块水晶石作为她礼物的原材料。

同时亚瑟斯有一种魔法,如果这两块长方形水晶石在某一个面能够完美的契合在一起(完美的契合是指这两个长方形面全等),那么可以将它们融合成一块完整的大石头,如果真的实现的话,那么久莲就可能打磨出更大的水晶球啦!

久莲太希望把最美最大的水晶球送给他了,你快帮帮她如何选择吧。

Input

第一行输入一个正整数n(1\leq n\leq 1e5)

在接下来n行中,第i行输入三个正整数a_i,b_i,c_i(1\leq a_i,b_i,c_i\leq 1e9)表示第i块水晶的长宽高,注意可能有两个长得一模一样的水晶石,但是在这种情况下还是将它们视作是两块不同的水晶石。

Output

第一行请输出一个正整数k, k= 1或2,表示久莲选择的水晶球数量。

第二行请输出k个正整数,如果k=1,请输出一个正整数x(1\leq x\leq n)表示久莲选择的水晶石。如果k=2,则请输出两个正整数 x,y(1\leq x,y\leq n)(用空格间隔),表示久莲希望亚瑟斯帮她将编号为x,y

的水晶石融合成一块更大的水晶石,并选择用这块水晶石来打磨加工。请注意,这两块水晶石必须满足 “完美契合” 的条件,否则这个选择不合法。如果有多种最优的选择,则你可以输出任意一种合法的最优方案。

Hint

对于样例,如果久莲选择第六个水晶球,那么她可以打磨成半径为r=2.5 的水晶球,这是最优的选择。

测试用例 1以文本方式显示
  1. 6↵
  2. 1 2 3↵
  3. 4 1 1↵
  4. 4 2 3↵
  5. 4 2 3↵
  6. 4 3 3↵
  7. 5 5 5↵
以文本方式显示
  1. 1↵
  2. 6↵
1秒64M

题意分析:

        不用说应该也能猜到,这题还是排序+贪心的思路。(废话,周标题都写在那了)

        我们先不考虑融合的情况,先来研究一下怎么样才能打磨出最大的水晶球吧。根据我们的初中数学知识,不难知道,长方体内取一个最大的球,球的半径应该等于长方体最短边的长度,所以不考虑融合的时候,我们只关注长方体最短的那个边,这是这题贪心思想的一个体现。

        那么考虑融合了会怎么样呢?假设长方体有三条边,从小到大依次是x,y,z,那么融合只有三种可能:x,y相等,x,z相等,y,z相等。然而前两种融合方法,并不会改变最短边依然是x的事实,所以真正有效的融合方法应该是第三种,融合后的最短边为min(x_1+x_2,\,y,\,z)

        好了,这题基本上已经做完了,接下来仅仅需要考虑的就是如何搜索那些可以融合的水晶了。

        首先,O(n^2)的暴力搜索,我们直觉上就觉得不行——因为这个融合的条件显然是可控的,并不是那么漫无目的地乱搜。融合的必要条件是“最长的两边”相等(因为我们只考虑上述第三种融合情况了,前两种融合情况对于寻找更优解没有意义)。换句话说,如果我们按照最长边、次长边、最短边的优先级来排序,所有能够融合的水晶都是扎堆出现的;而且,这些扎堆出现的y,z相等的水晶们里面,我们只需要考虑后两个——因为他们是x最大的两个,对于相同的y,z,我们只需要考虑他们就足够了,前面的那些无论如何表现不会比他们更好,这也是贪心思想的体现。

        具体的实现应该不难了,我就放在下面的代码和伪代码里,如果还没有读明白的读者可以多琢磨琢磨,贪心的题目就是这样,你可能看了半天都没看懂,忽然某个瞬间就顿悟了。


伪代码:

        读入数据,对每个数据内部进行排序,保证x\leq y\leq z,再将所有的数据保存到一个容器里;

        对容器按照z,y,x的优先级排序;//和上一题类似,可以重载“<”操作符,直接sort排序

        维护一个target变量用于保存我们选定的球的相关信息;//需要保存哪些信息?请读者自行思考

        遍历容器:

                如果某单个水晶的最小边就已经大于target保存的半径,更新target

                同时检查一下这个单个的水晶是否能与其他水晶匹配,方法就是看他的下一个水晶的y,z是否与他相等,如果是的话,不断往后搜索直到找到这一堆y,z相等的水晶的最后两个,他们就是这一堆能够合成的最大的水晶;

                比较合成后的水晶和target,如果更大,更新target

         遍历结束后,输出target    ;


贴代码:

    #include <bits/stdc++.h>  
    using namespace std;  
    typedef long long ll;  
    const int INF = 0x3f3f3f;  
      
    struct ball{   //用于保存水晶,同时要记录水晶的原始下标,防止排序后被打乱
        int x = 0;  
        int y = 0;  
        int z = 0;  
          //ensure that x <= y <= z  
        int index1 = -1;  
        int index2 = -1;  
    }temp, target;  
      
    inline void bSort(ball& ball){  //对水晶内部的三个参数进行排序,保证x <= y <= z  
        int t;  
        if(ball.x > ball.y){  
            t = ball.y;  
            ball.y = ball.x;  
            ball.x = t;  
        }  
        if(ball.y > ball.z){  
            t = ball.z;  
            ball.z = ball.y;  
            ball.y = t;  
        }  
        if(ball.x > ball.y){  
            t = ball.y;  
            ball.y = ball.x;  
            ball.x = t;  
        }  
    }  
      
    struct cmp{  //重载“<”操作符,用来给sort函数使用
        bool operator()(ball p, ball q){  
            if(p.z < q.z)  
                return true;  
            else if(p.z == q.z){  
                if(p.y < q.y)  
                    return true;  
                else if(p.y == q.y){  
                    if(p.x < q.x)  
                        return true;  
                    else  
                        return false;  
                }  
                else return false;  
            }  
            else return false;  
        }  
    };  
      
    int main(){  
        ///ifstream infile("input.txt", ios::in);  
        ///ofstream outfile("output.txt", ios::out);  
      
        vector<ball> list;  
        int n;  
        cin >> n;  
        for(int i = 0; i < n; i++){  
            cin >> temp.x >> temp.y >> temp.z;  
            temp.index1 = i;  
            bSort(temp);  
            list.push_back(temp);  
        }  
        sort(list.begin(), list.end(), cmp());  
        /*for(int i = 0; i < n; i++){ 
            cout << list[i].x << ' ' << list[i].y << ' ' << list[i].z << endl; 
        }*/  
      
        int pt = 0;  
        bool canCompound = false;  
        while(pt != n){  
            if(list[pt].x > target.x) {  //更新target
                target.x = list[pt].x;  
                target.index1 = list[pt].index1;  
                target.index2 = -1;  
            }  
            canCompound = false;  
            //cout << list[pt].y << ' ' << list[pt+1].y << ' ' << list[pt].z << ' ' << list[pt+1].z << endl;  
            while(list[pt].y == list[pt+1].y and list[pt].z == list[pt+1].z){  
                //寻找能够融合的一组水晶中x最大的两个
                canCompound = true;  
                pt++;  
                if(pt == n - 1) break;  
            }  
            if(canCompound){  //更新target
                //if(list[pt].y < target.x) break;  
                temp.index1 = list[pt].index1;  
                temp.index2 = list[pt-1].index1;  
                temp.x = list[pt].x + list[pt-1].x;  
                temp.y = list[pt].y;  
                temp.z = list[pt].z;  
                bSort(temp);  
                if(temp.x > target.x) target = temp;  
            }  
            pt++;  
            if(pt == n) break;  
        }  
        
         //输出
        if(target.index2 == -1){  
            cout << 1 << endl << target.index1 + 1 << endl;  
        }  
        else{  
            if(target.index1 < target.index2)  
                cout << 2 << endl << target.index1 + 1 << ' ' << target.index2 + 1 << endl;  
            else  
                cout << 2 << endl << target.index2 + 1 << ' ' << target.index1 + 1 << endl;  
        }  
      
        return 0;  
    }  

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里之码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值