完美对——阿里笔试题

题目


1.完美对 有n个物品,每个物品有k个属性,第i件物品的第j个属性用一个正整数表示记为,两个不同的物品讠,j被称为是完美对的当目仅当ai1+aj1=ai2+aj2=...=aik+ajk,求完美对的个数。 第一行两个数字n,k。 接下来n行,每行k个数字表示。

baoli

输入例子:
5 3 
2 11 21
19 10 1 
20 11 1 
6 15 24 
18 27 36 
输出例子:
 3

暴力破解


我第一反应就暴力破解,做好了超时的准备

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            int count=0;
            if(a==1)
                System.out.println(0);
            else{    
            int[][] c=new int[a][b];
            for(int i=0;i<a;i++){
                for(int j=0;j<b;j++){
                    c[i][j]=in.nextInt();
                }
            }
            for(int i=0;i<a;i++){
                for(int j=i+1;j<a;j++){
                    int sum=c[i][0]+c[j][0];
                    for(int k=0;k<b;k++){
                        if(sum!=c[i][k]+c[j][k]){
                            break;
                        }
                        if(k==b-1){
                            count++;
                        }                            
                    }
                }
            }
            System.out.println(count);
            }
        }

    }
}

结果不出我所料,超时了,因为时间复杂度已经达到了O(n^3)级别了。

优化思路


其实暴力破解的时候就想到了做减法,因为把等式两边移项就是同一列了。

原式    

等值变换
ai,1+aj,1=ai,2+aj,2  ai,1-ai,2=aj,2-aj,1
ai,2+aj,2=ai,3+aj,3 ai,2-ai,3=aj,3-aj,2
ai,3+aj,3=ai,4+aj,4  ai,3-ai,4=aj,4-aj,3
......
ai,k-1+aj,k-1=ai,k+aj,k    ai,k-1-ai,k=aj,k-aj,k-1

这样子相邻数做差,如果是完美对,他的数值绝对值是一样的。我们取正数然后拼接字符串,如果字符串一样且相邻数做差所得数组的和相加为0,就是完美对。

import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            int count=0;
             int[][] c=new int[a][b];
            String[]code=new String[a];
            for(int i=0;i<a;i++){
                c[i][0]=in.nextInt();
                StringBuilder s=new StringBuilder();
                for(int j=1;j<b;j++){
                    c[i][j]=in.nextInt();
                    int aa=c[i][j]-c[i][j-1];

                    if(aa>0)  
                        s.append("+"+aa);
                    else
                        s.append(aa);
                }
                code[i]=s.toString();
            }
            for(int i=0;i<a;i++){
                 String a2 =code[i].replaceAll("\\+","#");
                   a2=a2.replaceAll("-", "@");
                for(int j=i+1;j<a;j++){
                    String a1 =code[j].replaceAll("-","#");
                    a1 =a1.replaceAll("\\+","@");
                    if(a2.equals(a1)){
                        count++;
                    }
            }
            }
            System.out.println(count);
        }

    }
}

因为+变成-,再把-变成+的话,就全部变成+了(第一步的+又变回了+),所以用其他符号代替了。结果还是超时了,我用了哈希表优化,并且自己写了replaceAll方法。

import java.util.Scanner;
import java.util.HashMap;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static String change(String str) {
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < str.length(); i++) {
            char c = str.charAt(i);
            if (c == '+') {
                result.append('-');
            } else if (c == '-') {
                result.append('+');
            } else {
                result.append(c);
            }
        }
        return result.toString();
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
                      
            HashMap<String, Integer> codeCount = new HashMap<>();

            int count=0;
             int[][] c=new int[a][b];
            String[]code=new String[a];
            for(int i=0;i<a;i++){
                c[i][0]=in.nextInt();
                StringBuilder s=new StringBuilder();
                for(int j=1;j<b;j++){
                    c[i][j]=in.nextInt();
                    int aa=c[i][j]-c[i][j-1];
                    if(aa>0)  
                        s.append("+"+aa);
                    else
                        s.append(aa);
                }
                code[i]=s.toString();
                codeCount.compute(code[i], (key, value) -> (value == null) ? 1 : value + 1);

            }
             for(int i=0;i<a;i++){
                String j=change(code[i]);
                if(codeCount.containsKey(j)){
                    count+=codeCount.get(code[i])*codeCount.get(j);
                    codeCount.remove(j);
                    codeCount.remove(code[i]);
                }
             }

            System.out.println(count);
            
        }

    }
}

第一组数据就报错了,不知道为什么,凌晨1点了直接倒头睡觉了,梦里都在想怎么回事。
 



今天起床用自测数据试了一下,发现是等值数组会出现问题,因为其他都是两个不同的codeCount,但是如果测试用例下面这种情况:
 

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

那么理论输出是10,结果输出的是5*5/2=12了,因此要考虑到这种特殊情况,全0的codeCount是同一个,它的计算方法是C的n取2,因此改进代码如下:


AC代码

import java.util.Scanner;
import java.util.HashMap;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextInt()) { // 注意 while 处理多个 case
            int a = in.nextInt();
            int b = in.nextInt();
            HashMap<String, Integer> codeCount = new HashMap<>();
            int count=0;
             int[][] c=new int[a][b];
            String[]code=new String[a];
            for(int i=0;i<a;i++){
                c[i][0]=in.nextInt();
                StringBuilder s=new StringBuilder();
                StringBuilder sf=new StringBuilder();
                for(int j=1;j<b;j++){
                    c[i][j]=in.nextInt();
                    int aa=c[i][j]-c[i][j-1];
                    s.append(aa);
                    sf.append(-aa);
                }
                code[i]=s.toString();
                codeCount.compute(code[i], (key, value) -> (value == null) ? 1 : value + 1);
                code[i]=sf.toString();
            }
             for(int i=0;i<a;i++){
                String j=code[i];
                if(codeCount.containsKey(j)){
                    if(j.replaceAll("0", "").length() == 0){
                        count+=codeCount.get(j)*(codeCount.get(j)-1);
                        codeCount.remove(j);
                    }
                    else
                        count+=codeCount.get(j);
                }
             }

            System.out.println(count/2);
            
        }

    }
}

终于也是通过了所有的案例点了,家人们,不容易啊

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值