蓝桥杯 算法训练 审美课 java

Algo194 算法训练 审美课

题目如下:

image-20200716151742057

这道题如果直接暴力是完全不可能的,最大范围是 n <= 50000 , m <= 20

这道题如果是纯暴力的话 应该是(1 + 2 … + n ) * m 的一个复杂度,最多会到好几亿的,但只给你1秒,所以不可能暴力的

这道题的算法思想我也不知道是啥,我简单说下我的思路。

首先从这m入手,怎么能把这m变成1呢,变成1就只剩(1 + 2 … + n),就算n为50000也可以1秒跑完。

把m变成1就是一次就能比出两个同学是否完全相反,那就要把m个画变成一个东西,一个独一无二的东西。

看着例子 1 0 ,0 1 ,1 0 这不就是个二进制的01串吗,完全可以将这一串转换成一个十进制数。这样不仅保证唯一,也能一次比完。最大也就是2的20次,一百多万而已,int足以

下面的代码是第一次的写法,随便把(1 + 2 … + n )优化成n了,因为(1 + 2 … + n)最后两组超时了

package algo;

import java.util.Scanner;

/**
 * @Description: 算法训练 审美课
 * @ClassName: Algo194
 * @author: fan.yang
 * @date: 2020/07/16 11:51
 */
public class Algo194 {

    /**
     * 刚开始这样写的  90分 最后一组超内存了 可惜 这里我当时是认为数组写太多占内存了 750ms 300mb
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt(); 
        int[] array1 = new int[m];
        int[] judgeArray = new int[1024 * 1024];
        int k = 1;
        //1 2 4 8... 要转化成十进制 所以提前准备好每位的数
        for(int i = 0;i < m;i++){
            array1[i] = k;
            k *= 2;
        }
        //这个二维数组是这个意思 n可以看出这是每个学生都有的,每个学生可以存两个数 
        //第一个数是他自己那个01串转的十进制,第二个就是完全跟他相反的01串转的十进制,方便最后的判断
        int[][] array2 = new int[n][2];
        for(int i = 0;i < n;i++){
            int sum1 = 0;
            int sum2 = 0;
            for(int j = 0;j < m;j++){
                //如果是1就是给他自己那个十进制加上去 反之就给相反的加
                if(scanner.nextInt() == 1){
                    sum1 += array1[j];
                }else{
                    sum2 += array1[j];
                }
            }
            array2[i][0] = sum1;
            //这个就是统计转成十进制后都是sum1学生的数量 往前推就是他们01串是一模一样的
            //这也是为了方便后面的统计
            judgeArray[sum1]++;
            array2[i][1] = sum2;
        }
        int result = 0;
        //这里优化了下一个循环结束
        for(int i = 0;i < n;i++){
            //加上与某个同学完全相反的学生数量
            result += judgeArray[array2[i][1]];
            //加上后要把这个同学所在的那个数量减1 就代表这个学生已经比过了 不然跟他完全相反的同学来比对就会多一个 重复了
            judgeArray[array2[i][0]]--;
        }
        System.out.println(result);
    }

}
/*
现在用例子来说明
比如
3 2
1 0
0 1
1 0
同学1 是 1 0 字串  转化为十进制就是 1 * 1 + 0 * 2 = 1 其相反值为 0 * 1 + 1 * 2 = 2
同学2 是 0 1 字串  转化为十进制就是 0 * 1 + 1 * 2 = 2 其相反值为 1 * 1 + 0 * 2 = 1
同学3 是 1 0 字串  转化为十进制就是 1 * 1 + 0 * 2 = 1 其相反值为 0 * 1 + 1 * 2 = 2
这时候 judgeArray[1] = 2 judgeArray[2] = 1
最后循环统计
同学1 开始统计 发现 judgeArray[2] = 1  所以 rusult + 1 = 1  因为同学1统计过了 所以judgeArray[1]-- 
同学2 开始统计 发现 judgeArray[1] = 1  所以 rusult + 1 = 2  因为同学2统计过了 所以judgeArray[2]--
同学3 开始统计 发现 judgeArray[2] = 0  所以 rusult + 0 = 2  因为同学3统计过了 所以judgeArray[1]-- 
最后结果就是 2 就是这样一个过程
*/

好了 问题就来了 我个人认为上面这个算法还可以了,果然时间是够了 700ms就结束了 但内存却超了

我当时认为是数组多了,所以我尝试优化了一波,这里的优化就是去掉了array2数组,不需要去知道每个学生其自己的数以及与其相反的数,只需要知道每个数的学生有多少。因为我仔细想了下,自己的数加上完全相反的数得到的其实是全是1的二进制串转的十进制,比如01和10转成十进制分别是2和1(我是从左开始),相加不刚好等于11转的3吗。这样只要知道某个数就一定知道其相反的数,同时这个两个数所有的同学数量相乘就是所要的对数

package algo;

import java.util.Scanner;

/**
 * @Description: 算法训练 审美课
 * @ClassName: Algo194
 * @author: fan.yang
 * @date: 2020/07/16 11:51
 */
public class Algo194 {

    /**
     * 第二种写法 其实就是优化了一下算法 把那个二维数组去掉了 但发现内存还是跟第一种差不多 90分 fuck 750ms 300mb
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[] array1 = new int[m];
        int[] judgeArray = new int[1024 * 1024];
        int k = 1;
        int num = 0;
        //1 2 4 8...
        for(int i = 0;i < m;i++){
            num += k;
            array1[i] = k;
            k *= 2;
        }
        for(int i = 0;i < n;i++){
            int sum = 0;
            for(int j = 0;j < m;j++){
                if(scanner.nextInt() == 1){
                    sum += array1[j];
                }
            }
            //这里优化了一波 直接知道每个数的学生数量即可
            judgeArray[sum]++;
        }
        int result = 0;
        for(int i = 0;i <= num / 2;i++){
            //然后知道了 数 + 相反数 = num ,并且两个数的数量相差就是对数
            result += judgeArray[i] * judgeArray[num - i];
        }
        System.out.println(result);
    }
}
/*
例子如下
8 2
1 0
0 1
1 0
1 1
0 0
1 0
0 1
0 0
可以得出0的学生数为2,1的学生数为3,2的学生数为2,3的学生数为1 num = 3 (这个3是这样来的 m=2 所以就两位2进制 最大值就是 1 * 2 + 1 * 1)
所以就算 0 和 3 的学生数相乘 2 * 1 = 2 加上 1 和 2的学生数学生数相乘 3 * 2 = 6 结果就是8
*/

结果悲剧了,内存还是超了 fuck,那肯定不是我算法的问题了,我的算法效率已经ok,那唯一的问题出在了scanner,我是把scanner放在两层循环里,这样在最后一组数据里,会有50000 * 20次输入。怪不得爆掉

package algo;

import java.util.Scanner;

/**
 * @Description: 算法训练 审美课
 * @ClassName: Algo194
 * @author: fan.yang
 * @date: 2020/07/16 11:51
 */
public class Algo194 {

    /**
     * 第三种 考虑到应该不是数组太多或者操作太多 那就是scanner的问题了 最后一组预计是50000 * 20 1000000次输入估计
     * 内存就爆掉了 所以决定每个学生只输入一次 按行取 比如最后一组我省了950000次的输入
     * 562ms 127mb 虽然用到大量的String 但相比Scanner还是少了
     */
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int m = scanner.nextInt();
        int[] array1 = new int[m];
        int[] judgeArray = new int[1024 * 1024];
        int k = 1;
        int num = 0;
        //1 2 4 8...
        for(int i = 0;i < m;i++){
            num += k;
            array1[i] = k;
            k *= 2;
        }
        //这个要注意
        scanner.nextLine();
        for(int i = 0;i < n;i++){
            int sum = 0;
            String str = scanner.nextLine();
            String[] strArray = str.split(" ");
            for(int j = 0;j < strArray.length;j++){
                if("1".equals(strArray[j])){
                    sum += array1[j];
                }
            }
            judgeArray[sum]++;
        }
        int result = 0;
        for(int i = 0;i <= num / 2;i++){
            result += judgeArray[i] * judgeArray[num - i];
        }
        System.out.println(result);
    }
    
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值