完美对-牛客网

完美对

题目描述

有 n 个物品,每个物品有 k 个属性,第 i 件物品的第 j 个属性用一个正整数表示为 ai,j,两个不同的物品 i,j 被称为完美对当且仅当 ai,2 + aj,1= ai,2+ aj,2=···=ai,k + aj,k,求完美数的个数。

输入描述

第一行两个数字n,k.

接下来n行,第i行k个数字表示

ai,1,ai,2,···ai,k

1<=n<=10^5, 2<=k<=10, 1<ai<=100

输出描述

一行一个数字表示答案

示例1

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

解题思路

第一想法是直接暴力法,任选两组数组,接着依次从1,2,3···k判断是否满足条件即可。同时做好提交超时的心理准备,因为时间复杂度已经达到了O(n^3)级别了。

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // n,k
        int n = in.nextInt(), k = in.nextInt();
        //二维数组a 
        int[][] a = new int[n][k];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                a[i][j] = in.nextInt();

            }
        }
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (check(a, i, j, k)) {
                    res++;
                }
            }
        }
        System.out.println(res);
    }
    //判断两数组是否满足完美对的条件
    private static boolean check(int[][] a, int i, int j, int k) {
        int sum = a[i][0] + a[j][0];
        for (int idx = 1; idx < k; idx++) {
            if (a[i][idx] + a[j][idx] != sum) {
                return false;
            }
        }
        return true;
    }
}

超时了,那么就考虑如何优化?首先需要注意的是,无论如何优化算法,算法的时间复杂度一定是介于O(n^2)O(n^3)之间。接着,如何优化其实是个数学问题:

原式等值变换
ai,1+aj,1=ai,2+aj,2ai,1-ai,2=aj,2-aj,1
ai,2+aj,2=ai,3+aj,3ai,2-ai,3=aj,3-aj,2
ai,3+aj,3=ai,4+aj,4ai,3-ai,4=aj,4-aj,3
······
ai,k-1+aj,k-1=ai,k+aj,kai,k-1-ai,k=aj,k-aj,k-1

原式并没有什么特点,主要看右侧 等值变换的式子。然后把等值变换的式子从上至下相加:

(ai,1-ai,2)+(ai,2-ai,3)+(ai,3-aj,4)+···+(ai,k-1-ai,k)=(aj,2-aj,1)+(aj,3-aj,2)+(aj,4-aj,3)+···+(aj,k-aj,k-1)

不妨设Li =(ai,1-ai,2)+(ai,2-ai,3)+(ai,3-aj,4)+···+(ai,k-1-ai,k) ,则有 -Lj=(aj,2-aj,1)+(aj,3-aj,2)+(aj,4-aj,3)+···+(aj,k-aj,k-1)

根据上述推到,对于完美对i,j来说,一定有 Li = - Lj成立。换言之,若Li != Lj,那么i,j对必然不是完美对

下述代码正是借助L排除掉不符合条件的数组对实现的:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        //n,k 
        int n = in.nextInt(), k = in.nextInt();
        //二维数组a 
        int[][] a = new int[n][k];
        //L条件
        int[] L = new int[n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < k; j++) {
                a[i][j] = in.nextInt();
                if (j > 0) {
                    //构建L
                    L[i] += a[i][j - 1] - a[i][j];
                }
            }
        }
        int res = 0;
        //L值与下标列表 映射
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int i = 0; i < n; i++) {
            //过滤不符合L条件
            if (map.containsKey(-L[i])) {
                for (Integer e : map.get(-L[i])) {
                    if (check(a, i, e, k)) {
                        res++;
                    }
                }
            }
            if (!map.containsKey(L[i])) {
                map.put(L[i], new ArrayList<>());
            }
            map.get(L[i]).add(i);
        }
        System.out.println(res);
    }

    private static boolean check(int[][] a, int i, int j, int k) {
        int sum = a[i][0] + a[j][0];
        for (int idx = 1; idx < k; idx++) {
            if (a[i][idx] + a[j][idx] != sum) {
                return false;
            }
        }
        return true;
    }
}

各位看官,如果觉得我解释的还算清楚,就给我点个赞吧!(骗赞侠,哈哈)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值