• 2021秋季算法入门班第五章习题:优先队列、并查集 1027 [NOIP2017]奶酪


    1027-[NOIP2017]奶酪_2021秋季算法入门班第五章习题:优先队列、并查集 (nowcoder.com)

    题意:
    现有一块大奶酪,它的高度为 h,它的长度和宽度我们可以认为是无限大的,奶酪中间有许多半径相同的球形空洞。我们可以在这块奶酪中建立空间坐标系, 在坐标系中,奶酪的下表面为 z = 0,奶酪的上表面为 z = h。
    现在, 奶酪的下表面有一只小老鼠 Jerry, 它知道奶酪中所有空洞的球心所在的坐标。如果两个空洞相切或是相交,则 Jerry 可以从其中一个空洞跑到另一个空洞,特别地,如果一个空洞与下表面相切或是相交, Jerry 则可以从奶酪下表面跑进空洞; 如果一个空洞与上表面相切或是相交, Jerry 则可以从空洞跑到奶酪上表面。
    位于奶酪下表面的 Jerry 想知道, 在不破坏奶酪的情况下,能否利用已有的空洞跑到奶酪的上表面去?
    空间内两点 P1(x1,y1,z1) 、P2(x2,y2,z2) 的距离公式如下:

    思路:

    并查集模板题

    如果两个空心球相交,就合并

    然后统计有多少个这样的通道连着天连着地的

    Code:

    1. #include
    2. using namespace std;
    3. const int mxn=1e3+10;
    4. typedef long long ll;
    5. #define int ll
    6. int n,h,r,len1=0,len2=0;
    7. int tp[mxn],dw[mxn],f[mxn];
    8. struct ty{
    9. int x,y,z,id;
    10. }a[mxn];
    11. int find(int x){
    12. return f[x]=(x==f[x]?x:find(f[x]));
    13. }
    14. void join(int x,int y){
    15. int f1=find(x),f2=find(y);
    16. if(f1!=f2) f[f1]=f2;
    17. }
    18. int dis(int x1,int y1,int z1,int x2,int y2,int z2){
    19. return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2);
    20. }
    21. void init(){
    22. len1=len2=0;
    23. for(int i=0;i<=n;i++){
    24. tp[i]=dw[i]=0;
    25. }
    26. }
    27. void solve(){
    28. scanf("%lld%lld%lld",&n,&h,&r);
    29. init();
    30. for(int i=1;i<=n;i++){
    31. f[i]=i;
    32. a[i].id=i;
    33. scanf("%lld%lld%lld",&a[i].x,&a[i].y,&a[i].z);
    34. if(h-a[i].z<=r) tp[++len1]=a[i].id;
    35. if(a[i].z<=r) dw[++len2]=a[i].id;
    36. }
    37. for(int i=1;i<=n;i++){
    38. for(int j=i+1;j<=n;j++){
    39. if((a[i].x-a[j].x)*(a[i].x-a[j].x)+(a[i].y-a[j].y)*(a[i].y-a[j].y)>=4*r*r) continue;
    40. if(dis(a[i].x,a[i].y,a[i].z,a[j].x,a[j].y,a[j].z)<=4*r*r){
    41. join(a[i].id,a[j].id);
    42. }
    43. }
    44. }
    45. for(int i=1;i<=len1;i++){
    46. for(int j=1;j<=len2;j++){
    47. if(find(tp[i])==find(dw[j])){
    48. puts("Yes");
    49. return;
    50. }
    51. }
    52. }
    53. puts("No");
    54. }
    55. signed main(){
    56. int T;
    57. scanf("%lld",&T);
    58. while(T--) solve();
    59. return 0;
    60. }

    总结:

    算法:

    什么时候用并查集:

    当我们只关注一些事物的分类关系或者出现合并关系时

    并查集写法:

    大多数并查集都是将它们的id合并起来

    思维:

    仅当通道贯穿天和地时表明可以通过空洞穿越整个奶酪

    即与天或地有接触的空洞之间存在祖先相同就说明属于同一类,即有通道贯穿天和地

    因此去维护和天或和地有接触的空洞的id即可

    细节:

    中间有个剪枝,防止爆ll

  • 相关阅读:
    ssh免密登录的原理RSA非对称加密的理解
    Spring RabbitMQ那些事(1-交换机配置&消息发送订阅实操)
    .NET Redis限制接口请求频率 滑动窗口算法
    2022-9-20-C++11新特性
    Vue插槽
    Redis之发布订阅
    python自动化之Python webservice协议
    微信小程序云开发教程——墨刀原型工具入门(页面交互+交互案例教程)
    异或(xor)的讲解和使用方法
    c# 多线程处理
  • 原文地址:https://blog.csdn.net/weixin_62528401/article/details/126454807