ลบรายการออกจากอาร์เรย์เพื่อจัดเรียงและเพิ่มผลรวมขององค์ประกอบ


13

ความท้าทายนี้มาจากการทดสอบการรับเข้าเรียนจนถึงหลักสูตรความปลอดภัยทางไซเบอร์ที่ปิด อย่างไรก็ตามมันไม่ได้เกี่ยวข้องกับการรักษาความปลอดภัยในโลกไซเบอร์ แต่เพียงเพื่อทดสอบทักษะการเขียนโปรแกรมและตรรกะของนักเรียน

งาน

เขียนโปรแกรมที่ลบรายการออกจากอาร์เรย์เพื่อให้ค่าที่เหลือถูกเรียงลำดับในลำดับที่ลดลงอย่างเคร่งครัดและผลรวมของพวกเขาคือค่าสูงสุดในลำดับการลดที่เป็นไปได้อื่น ๆ ทั้งหมด

อินพุตและเอาต์พุต

การป้อนข้อมูลจะมีอาร์เรย์ของค่าจำนวนเต็มอย่างเคร่งครัดมากขึ้นกว่า0และแตกต่างจากคนอื่น ๆ คุณมีอิสระที่จะเลือกว่าจะอ่านอินพุตจากไฟล์บรรทัดคำสั่งหรือ stdin

เอาต์พุตจะเป็นซับราเรย์ที่เรียงลำดับจากมากไปน้อยของอินพุตหนึ่งซึ่งผลรวมนั้นมีค่าสูงกว่าซับราเรย์ย่อยเรียงจากมากไปน้อยอื่น ๆ ที่เป็นไปได้

หมายเหตุ: [5, 4, 3, 2]เป็นระบบย่อยของ[5, 4, 1, 3, 2]แม้ว่า4และ3จะไม่ติดกัน เพียงเพราะ1ถูกตอก

โซลูชั่น Bruteforce

วิธีแก้ปัญหาที่ง่ายที่สุดของหลักสูตรจะวนซ้ำในชุดค่าผสมที่เป็นไปได้ทั้งหมดของอาร์เรย์ที่กำหนดและค้นหาชุดข้อมูลที่เรียงลำดับด้วยผลรวมที่ยิ่งใหญ่ที่สุดซึ่งจะเป็นในPython :

import itertools

def best_sum_desc_subarray(ary):
    best_sum_so_far = 0
    best_subarray_so_far = []
    for k in range(1, len(ary)):
        for comb in itertools.combinations(ary, k):
            if sum(comb) > best_sum_so_far and all(comb[j] > comb[j+1] for j in range(len(comb)-1)):
                best_subarray_so_far = list(comb)
                best_sum_so_far = sum(comb)
    return best_subarray_so_far

น่าเสียดายเนื่องจากการตรวจสอบว่าอาร์เรย์เรียงลำดับและคำนวณผลรวมขององค์ประกอบหรือไม่และเนื่องจากการดำเนินการนี้จะทำเวลาตั้งแต่ไปจนถึงความซับซ้อนของเวลาแบบอะซิมโทติค

ท้าทาย

เป้าหมายของคุณคือการทำให้เวลามีความซับซ้อนดีขึ้น การแก้ปัญหาด้วยเวลาที่ซับซ้อนเชิงเวลาน้อยที่สุดคือผู้ชนะของการท้าทาย หากสองวิธีมีความซับซ้อนของเวลาแบบอะซิมโทติคผู้ชนะจะเป็นแบบที่มีความซับซ้อนเชิงพื้นที่แบบขนาดเล็กที่สุด

หมายเหตุ: คุณสามารถพิจารณาการอ่านการเขียนและการเปรียบเทียบอะตอมแม้เป็นจำนวนมาก

หมายเหตุ: หากมีสองวิธีหรือมากกว่านั้นให้ส่งคืนทั้งสองวิธี

กรณีทดสอบ

Input:  [200, 100, 400]
Output: [400]

Input:  [4, 3, 2, 1, 5]
Output: [4, 3, 2, 1]

Input:  [50, 40, 30, 20, 10]
Output: [50, 40, 30, 20, 10]

Input:  [389, 207, 155, 300, 299, 170, 158, 65]
Output: [389, 300, 299, 170, 158, 65]

Input:  [19, 20, 2, 18, 13, 14, 8, 9, 4, 6, 16, 1, 15, 12, 3, 7, 17, 5, 10, 11]
Output: [20, 18, 16, 15, 12, 7, 5]

Input:  [14, 12, 24, 21, 6, 10, 19, 1, 5, 8, 17, 7, 9, 15, 23, 20, 25, 11, 13, 4, 3, 22, 18, 2, 16]
Output: [24, 21, 19, 17, 15, 13, 4, 3, 2]

Input:  [25, 15, 3, 6, 24, 30, 23, 7, 1, 10, 16, 29, 12, 13, 22, 8, 17, 14, 20, 11, 9, 18, 28, 21, 26, 27, 4, 2, 19, 5]
Output: [25, 24, 23, 22, 17, 14, 11, 9, 4, 2]

ที่เกี่ยวข้อง (ฉันไม่สามารถตรวจสอบได้ในขณะนี้ว่าอัลกอริทึมทั้งสองนั้นมีความจริงเท่ากันหรือไม่ แต่ฉันคิดว่ามันอาจจะเป็น)
Martin Ender

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Martin Ender

คำตอบ:


3

Perl

นี่ควรเป็น O (n ^ 2) ในเวลาและ O (n) ในอวกาศ

ให้ตัวเลขคั่นด้วยช่องว่างในหนึ่งบรรทัดถึง STDIN

#!/usr/bin/perl -a
use strict;
use warnings;

# use Data::Dumper;
use constant {
    INFINITY => 9**9**9,
    DEBUG    => 0,
};

# Recover sequence from the 'how' linked list
sub how {
    my @z;
    for (my $h = shift->{how}; $h; $h = $h->[1]) {
        push @z, $h->[0];
    }
    pop @z;
    return join " ", reverse @z;
}

use constant MINIMUM => {
    how  => [-INFINITY, [INFINITY]],
    sum  => -INFINITY,
    next => undef,
};

# Candidates is a linked list of subsequences under consideration
# A given final element will only appear once in the list of candidates
# in combination with the best sum that can be achieved with that final element
# The list of candidates is reverse sorted by final element
my $candidates = {
    # 'how' will represent the sequence that adds up to the given sum as a
    # reversed lisp style list.
    # so e.g. "1, 5, 8" will be represented as [8, [5, [1, INFINITY]]]
    # So the final element will be at the front of 'how'
    how  => [INFINITY],
    # The highest sum that can be reached with any subsequence with the same
    # final element
    sum  => 0,
    # 'next' points to the next candidate
    next => MINIMUM,   # Dummy terminator to simplify program logic
};

for my $num (@F) {
    # Among the candidates on which an extension with $num is valid
    # find the highest sum
    my $max_sum = MINIMUM;
    my $c = \$candidates;
    while ($num < $$c->{how}[0]) {
        if ($$c->{sum} > $max_sum->{sum}) {
            $max_sum = $$c;
            $c = \$$c->{next};
        } else {
            # Remove pointless candidate
            $$c = $$c->{next};
        }
    }

    my $new_sum = $max_sum->{sum} + $num;
    if ($$c->{how}[0] != $num) {
        # Insert a new candidate with a never before seen end element
        # Due to the unique element rule this branch will always be taken
        $$c = { next => $$c };
    } elsif ($new_sum <= $$c->{sum}) {
        # An already known end element but the sum is no improvement
        next;
    }
    $$c->{sum} = $new_sum;
    $$c->{how} = [$num, $max_sum->{how}];
    # print(Dumper($candidates));
    if (DEBUG) {
        print "Adding $num\n";
        for (my $c = $candidates; $c; $c = $c->{next}) {
            printf "sum(%s) = %s\n", how($c), $c->{sum};
        }
        print "------\n";
    }
}

# Find the sequence with the highest sum among the candidates
my $max_sum = MINIMUM;
for (my $c = $candidates; $c; $c = $c->{next}) {
    $max_sum = $c if $c->{sum} > $max_sum->{sum};
}

# And finally print the result
print how($max_sum), "\n";

3

Haskell ,เวลาพื้นที่O ( n )O(nlogn)O(n)

{-# LANGUAGE MultiParamTypeClasses #-}

import qualified Data.FingerTree as F

data S = S
  { sSum :: Int
  , sArr :: [Int]
  } deriving (Show)

instance Monoid S where
  mempty = S 0 []
  mappend _ s = s

instance F.Measured S S where
  measure = id

bestSubarrays :: [Int] -> F.FingerTree S S
bestSubarrays [] = F.empty
bestSubarrays (x:xs) = left F.>< sNew F.<| right'
  where
    (left, right) = F.split (\s -> sArr s > [x]) (bestSubarrays xs)
    sLeft = F.measure left
    sNew = S (x + sSum sLeft) (x : sArr sLeft)
    right' = F.dropUntil (\s -> sSum s > sSum sNew) right

bestSubarray :: [Int] -> [Int]
bestSubarray = sArr . F.measure . bestSubarrays

มันทำงานอย่างไร

bestSubarrays xsคือลำดับของ subarrays ของxsที่อยู่ในขอบเขตที่มีประสิทธิภาพของ {ผลรวมที่ใหญ่ที่สุดองค์ประกอบแรกที่เล็กที่สุด} สั่งจากซ้ายไปขวาโดยการเพิ่มผลรวมและการเพิ่มองค์ประกอบแรก

ไปจากbestSubarrays xsไปbestSubarrays (x:xs)เรา

  1. แยกลำดับเป็นด้านซ้ายที่มีองค์ประกอบแรกที่น้อยกว่าxและด้านขวามีองค์ประกอบแรกมากกว่าx,
  2. ค้นหา subarray ใหม่โดยเพิ่มxไปยัง subarray ทางขวาสุดทางด้านซ้าย
  3. วางคำนำหน้าของ subarrays จากด้านขวาด้วยผลรวมเล็กกว่า subarray ใหม่
  4. ต่อกันทางซ้าย subarray ใหม่และส่วนที่เหลือของด้านขวา

ต้นไม้นิ้วสนับสนุนการดำเนินงานทั้งหมดเหล่านี้ในเวลาO(logn)


1

คำตอบนี้ขยายออกไปในคำสอนของ Ton Hospel

ปัญหาสามารถแก้ไขได้ด้วยการเขียนโปรแกรมแบบไดนามิกโดยใช้การเกิดซ้ำ

T(i)=ai+max[{0}{T(j)|0j<iaiaj}]

ที่เป็นลำดับป้อนข้อมูลและผลรวมที่ทำได้สูงสุดลดลงย่อยลำดับใด ๆ ที่ลงท้ายด้วยดัชนีฉันวิธีการแก้ปัญหาที่เกิดขึ้นจริงอาจถูก retraced โดยใช้เช่นเดียวกับในรหัสสนิมต่อไปนี้(ai)T(i)iT

fn solve(arr: &[usize]) -> Vec<usize> {
    let mut tbl = Vec::new();
    // Compute table with maximum sums of any valid sequence ending
    // with a given index i.
    for i in 0..arr.len() {
        let max = (0..i)
            .filter(|&j| arr[j] >= arr[i])
            .map(|j| tbl[j])
            .max()
            .unwrap_or(0);
        tbl.push(max + arr[i]);
    }
    // Reconstruct an optimal sequence.
    let mut sum = tbl.iter().max().unwrap_or(&0).clone();
    let mut limit = 0;
    let mut result = Vec::new();

    for i in (0..arr.len()).rev() {
        if tbl[i] == sum && arr[i] >= limit {
            limit = arr[i];
            sum -= arr[i];
            result.push(arr[i]);
        }
    }
    assert_eq!(sum, 0);
    result.reverse();
    result
}

fn read_input() -> Vec<usize> {
    use std::io::{Read, stdin};
    let mut s = String::new();
    stdin().read_to_string(&mut s).unwrap();
    s.split(|c: char| !c.is_numeric())
        .filter(|&s| !s.is_empty())
        .map(|s| s.parse().unwrap())
        .collect()
}

fn main() {
    println!("{:?}", solve(&read_input()));
}

ลองออนไลน์!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.