蓝桥杯 算法训练 娜神平衡 (Java)问题

问题描述:

娜娜是一个特别可爱的女孩子,作为学神的她最近在情感方面出现了一点点小问题。
  她暗恋的琦琦是一名学霸,他只喜欢长得漂亮和学习很好的女生。
  娜娜学习确实很神,但是她在琦琦面前却总是表现不出平时的神力。
  琦琦感受到了娜娜对他的爱,但是他还是觉得娜娜的学习并不是特别好,于是他出了一道题给娜 娜。
  “娜娜,我们之间的关系需要在不断深入的同时保持一定的平衡,不可以你总是强势或者我总是弱势。”
  琦琦给了娜娜一些两两不等的数,希望娜娜能把这些数分成两组A和B,满足以下条件:
  1:每一次只能操作一个数,即只取出一个数分入A中或B中;
  2:每一次操作完成后,A中数之和与B中数之和的差不能超过r。
  新时代的丘比特们啊,帮帮娜娜吧!

输入格式:

输入共两行。
  第一行包括两个正整数n和r,n表示琦琦一共给了n个数,r的意义见题目描述。
  第二行包括n个正整数,分别表示琦琦给的n个数。

输出格式:

输出共两行,分别把A与B两组数按从小到大输出。
  注意输入中n个数的第一个必须分入A组。
  琦琦保证这样的输出唯一。

样例输入:

4 10
9 6 4 20

样例输出:

4 6 9
20

样例说明:

先把4和6先后分入A组,再把20分入B组,最后把9分入A组。

数据规模和约定:

很小,真的很小。

思路:

  1. 本题使用dfs算法进行遍历,下面以本题的例子结合代码进行分析

  1. dfs算法实际上是递归的思想,因此我们先来分析终止递归的算法

if (find){
            return;
        }
        if (key >= n){
            if (flag){
                for (Object i : A){
                    System.out.print(i + " ");
                }

                System.out.println();

                for (Object i : B){
                    System.out.print(i + " ");
                }
               find = true;
            }
            return;
        }

当我们把A,B列表通过加强for循环输出后边将find赋值为真,这样当我们会到上一层的递归时就不会执行if(key >=n)的判断,而是直接执行完if(find)即可。而flag主要是需要满足题目的要求:需要输入的第一个元素必须放置到A列表中。

  1. 下面我们来分析dfs的主要逻辑代码!!!!~

for (int i = 0; i < n; i++){
            if (!vit[i]){
                if (abs(sumA + ori[i] - sumB) <= r){
                    A.add(ori[i]);
                    sumA += ori[i];
                    vit[i] = true;
                    if (ori[i] == num1){
                        flag = true;
                    }
                    dfs(key + 1);
                    A.remove(A.size()-1);
                    sumA -= ori[i];
                    vit[i] = false;
                    if (ori[i] == num1){
                        flag = true;
                    }
                }

                if (abs(sumB + ori[i] - sumA) <= r){
                    B.add(ori[i]);
                    sumB += ori[i];
                    vit[i] = true;
                    dfs(key + 1);
                    B.remove(B.size()-1);
                    sumB -= ori[i];
                    vit[i] = false;
                }
            }

        }

首先如果我们想要使用dfs算法,那就必须得具好一个与储存数据数组大小相同的布尔类型的数组。这样每当我们进行递归操作时对应每次的for循环我们就可以知道上一层dfs方法对数组元素是否有遍历或者调用过。根据题目的条件我们可以得出以下的结论:

sumA + ori[i] - sumB <= r 说明这个数可以存放到A列表中
sumB + ori[i] - sumA) <= r 说明这个数可以存放到B列表中

不难发现,在存放A和B的列表中,都可以看到调用dfs递归的身影,以本例题的输入为列,在原数组已经从小到大排好序之后。我们知道9是存入到B列表中去了,而到递归进入dfs对下一个元素20进行判断时,发现两个if都不满足条件。因此我们回到上一层,把这个元素清理出B列表,然后把该元素9对应位置的布尔值赋值为false。此时i的值为2,因此当再次判断是否满足A列表的条件中,这个数就存入到A列表中去了。对于B列表也是如此的道理。

  1. 对于abs方法,我这里是使用了位运算的法则,嫌麻烦的话可以直接调用Math.abs()方法

public static int abs(int a){
        return a + (a >> 31) ^ (a >> 31);
    }

完整代码:

import java.util.*;

public class Main {
    static int num1,sumA,sumB,n,r;
    static boolean flag = false,find = false;
    static List A;
    static List B;
    static boolean[] vit;
    static int[] ori;

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        n = scan.nextInt();
        r = scan.nextInt();
        vit = new boolean[n];
        ori = new int[n];
        A = new ArrayList();
        B = new ArrayList();
        for (int i = 0; i < n; i++){
            ori[i] = scan.nextInt();
        }

        num1 = ori[0];

        Arrays.sort(ori);


        dfs(0);

    }

    public static void dfs(int key){
        if (find){
            return;
        }
        if (key >= n){
            if (flag){
                for (Object i : A){
                    System.out.print(i + " ");
                }

                System.out.println();

                for (Object i : B){
                    System.out.print(i + " ");
                }
               find = true;
            }
            return;
        }


        for (int i = 0; i < n; i++){
            if (!vit[i]){
                if (abs(sumA + ori[i] - sumB) <= r){
                    A.add(ori[i]);
                    sumA += ori[i];
                    vit[i] = true;
                    if (ori[i] == num1){
                        flag = true;
                    }
                    dfs(key + 1);
                    A.remove(A.size()-1);
                    sumA -= ori[i];
                    vit[i] = false;
                    if (ori[i] == num1){
                        flag = true;
                    }
                }

                if (abs(sumB + ori[i] - sumA) <= r){
                    B.add(ori[i]);
                    sumB += ori[i];
                    vit[i] = true;
                    dfs(key + 1);
                    B.remove(B.size()-1);
                    sumB -= ori[i];
                    vit[i] = false;
                }
            }

        }
    }

    public static int abs(int a){
        return a + (a >> 31) ^ (a >> 31);
    }


}

测试结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Luca-s-

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

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

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

打赏作者

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

抵扣说明:

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

余额充值