【问题标题】:How to optimize beginner HackerEarth code如何优化初学者 HackerEarth 代码
【发布时间】:2018-12-21 23:59:57
【问题描述】:

我一直在尝试解决这个 HackerEarth 问题,但最后几个测试用例似乎总是超时

问题陈述: https://www.hackerearth.com/practice/algorithms/searching/linear-search/practice-problems/algorithm/joker-and-thieves-53e59f4a/

我是一年级学生,所以我真的不知道如何很好地优化

我尝试查看 java 解决方案,但它只是将更大的案例放入代码中,有效地消除了优化它的需要

import java.io.*;
import java.util.*;
public class police {
    static int t,n,k;
    static char[][] test;
    static int max = 0;
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        t = Integer.parseInt(st.nextToken());


        for (int i = 0; i < t; i++) {
           st = new StringTokenizer(br.readLine());
            n = Integer.parseInt(st.nextToken());
            k = Integer.parseInt(st.nextToken());
            test = new char[n][n];


            int ret = 0;

            for (int b = 0; b < n; b++) {
                st = new StringTokenizer(br.readLine());
                for (int a = 0; a < n; a++) {
                    test[b][a] = st.nextToken().charAt(0);
                }
            }

            for (int b = 0; b < n; b++) {
                ret += solve(test[b]); //calculate each row
            }

            System.out.println(ret);


        }    


    }
    static int solve(char[] a) { //given a row, calculate the maximum number of catches
        int ret = 0;
        for (int i = 0; i < n; i++) {
            if (a[i] == 'P') {

                for (int b = i - k; b <= i + k; b++) { //scan area to see if police can catch a thief
                    if (b >= 0 && b < n && a[b] == 'T') {
                        a[b] = 'C'; //caught
                        break;
                    }
                }

                a[i] = 'U'; //used
            }

        }
        for (int i = 0; i < n; i++) //count
            if (a[i] == 'C')
                ret++;

        return ret;
    }
}

我很确定这与解决方法有关,如果有人可以帮助我,那就太棒了

【问题讨论】:

  • JC Denton - 如此美好的时光!我能给你的唯一提示是单排并计算最大的窃贼。然后继续下一个
  • 嗨 - 我认为我的代码可以做到这一点,但它似乎真的很慢。当你有时间看看如何改进它时,你能检查一下我的“解决”方法吗?

标签: java algorithm optimization data-structures


【解决方案1】:

你是对的,你在 solve 方法中使用的方法是超时的原因。

首先你需要对algorithm complexityBig O notation有个概念

问题约束是:

1 &lt;= N &lt;= 1000

1 &lt;= K &lt;= N * N

这些表明您的解决方案的复杂性最多应为O(N * N)。换句话说,最多有两个嵌套的 for 循环,每个循环的复杂度为 O(N)。

在您的解决方案中,您正在执行以下操作:

for (int b = 0; b < n; b++) {
    ret += solve(test[b]); //calculate each row
}

好的,这个循环是必不可少的,因为您必须遍历网格中的所有行。复杂度:O(N)

然后在你的解决方法中:

for (int i = 0; i < n; i++) {
    if (a[i] == 'P') {

        for (int b = i - k; b <= i + k; b++) { //scan area to see if police can catch a thief
            if (b >= 0 && b < n && a[b] == 'T') {
                a[b] = 'C'; //caught
                break;
            }
        }

        a[i] = 'U'; //used
    }

}

这些嵌套的 for 循环是问题的真正原因,外部循环的复杂度是 O(N),而内部循环的复杂度也可能是 O(N),因为 K 的值很高。所以三个for循环的总复杂度可以达到O(N * N * N),肯定会超时。

这是我对这个问题的解决方案,我只修改了solve方法:

static int solve(char[] a) { //given a row, calculate the maximum number of catches
        int ret = 0;
        ArrayDeque<Integer> policeMenQueue = new ArrayDeque<>(); // queue for holding positions of policemen
        ArrayDeque<Integer> thievesQueue = new ArrayDeque<>(); // queue for positions of thieves
        for (int i = 0; i < n; i++) {
            if(!policeMenQueue.isEmpty()) { // check if the leftmost policeman can catch a thief at current position (i)
                Integer mostLeftPoliceMan = policeMenQueue.getFirst();
                if(i - mostLeftPoliceMan > k) { // if he cannot then we must remove him as he will no longer be able to catch any thieves
                    policeMenQueue.removeFirst();
                }
            }
            if(!thievesQueue.isEmpty()) { // check if the leftmost thief can be caught be a policeman at current position (i)
                Integer mostLeftThief = thievesQueue.getFirst();
                if(i - mostLeftThief > k) { // if not then we must remove him as he will no longer be caught by any policemen
                    thievesQueue.removeFirst();
                }
            }
            if(a[i] == 'P') {
                if(!thievesQueue.isEmpty()) { // the leftmost thief can be caught by current policeman
                    ret++;
                    thievesQueue.removeFirst(); // ok this thief is caught, remove him
                } else {
                    policeMenQueue.addLast(i); // just add the policeman to keep his position in the queue
                }
            }
            if(a[i] == 'T') {
                if(!policeMenQueue.isEmpty()) { // the current thief can be caught by the leftmost policeman
                    ret++;
                    policeMenQueue.removeFirst(); // ok this policeman has already caught a thief (used), remove him
                } else {
                    thievesQueue.addLast(i); // just add the thief to keep his position in the queue
                }
            }
        }
        return ret;
    }

我的想法:从左到右循环每一行,正如问题所述:每个警察只能抓到一个小偷,我们希望最大限度地增加抓到的小偷,所以每个小偷都被抓到对我们有利最左边的警察(我们从左到右)。

例如考虑这一行:

P P T T

想象一下K = 2

我们赞成3位置的小偷被1位置的警察抓到,因为这个警察无法抓住4位置的小偷,当然2位置的警察可以抓住两个小偷这种情况,但请记住,我们必须最大化抓到的小偷数量,每个警察只能抓到一个小偷,所以我们希望每个小偷都被最左边的那个能抓到他的警察抓到。

我的解决方案依赖于queue 数据结构(Java 中的ArrayDeque),如果您不知道它是如何工作的或者我为什么要使用它,请看这里:https://www.tutorialspoint.com/data_structures_algorithms/dsa_queue.htm

【讨论】:

  • 很好的解决方案!非常感谢您为我详细解释并评论您的代码,我现在正在研究队列数据结构
  • 我猜,使用两个索引(指向最后一个未使用但可用的警察/小偷可能更简单),但我真的很喜欢你的解决方案。
猜你喜欢
  • 2017-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-04
  • 1970-01-01
  • 1970-01-01
  • 2019-01-27
相关资源
最近更新 更多