Sıralamak ve öğelerin toplamını en üst düzeye çıkarmak için girişleri diziden kaldırın


13

Bu zorluk, kabul testinden kapalı siber güvenlik kursuna. Her neyse, siber güvenlik ile ilgili değil, sadece öğrencilerin mantıksal ve kodlama becerilerini test etmek.

Görev

Bir diziden girdileri kaldıran bir program yazın, böylece kalan değerler kesinlikle azalan bir sıraya göre sıralanır ve toplamları olası diğer azalan sıralar arasında en üst düzeye çıkarılır.

Giriş ve çıkış

Girdi , birbirinden kesinlikle daha büyük 0ve hepsi birbirinden farklı bir tamsayı değerleri dizisi olacaktır . Dosyadan, komut satırından veya stdin'den girdiyi okumayı seçebilirsiniz.

Çıkış , bir olacaktır inen-sıralanmış Toplam, diğer olası inen-kriteri alt dizisinde daha büyük olan giriş birine göre altdizilim.

Not: [5, 4, 3, 2] bir subarrav [5, 4, 1, 3, 2]bile, 4ve 3bitişik değildir. Çünkü 1patladı.

Bruteforce çözümü

Tabii ki en basit çözüm, verilen dizinin tüm olası kombinasyonları arasında yineleme yapmak ve Python'da en büyük toplamı olan sıralı bir tane aramak olacaktır :

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

Maalesef, dizi sıralanır eğer kontrol ve 's elemanlarının toplamını hesaplayarak beri edilir ve bu operasyon beri yapılacaktır için kez gelen üzere , asimptotik zaman karmaşıklığı olacak

Meydan okuma

Amacınız yukarıdaki kaba kuvvetten daha iyi bir zaman karmaşıklığı elde etmektir. En küçük asimptotik zaman karmaşıklığına sahip çözüm, mücadelenin kazananıdır. İki çözüm aynı asimtotik zaman karmaşıklığına sahipse, kazanan en küçük asimtotik uzamsal karmaşıklığa sahip olan olacaktır.

Not: Çok sayılarda bile atomik okumayı, yazmayı ve karşılaştırmayı düşünebilirsiniz .

Not: İki veya daha fazla çözüm varsa ikisinden birini döndürün.

Test senaryoları

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]

İlişkili. (Şu anda iki algoritmanın aslında eşdeğer olup olmadığını kontrol edemiyorum, ancak bence olabilirler.)
Martin Ender

Yorumlar uzun tartışmalar için değildir; bu görüşme sohbete taşındı .
Martin Ender

Yanıtlar:


3

Perl

Bu zaman içinde O (n ^ 2) ve uzayda O (n) olmalıdır

STDIN'a bir satırdaki boşlukla ayrılmış sayılar verin

#!/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

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

Nasıl çalışır

bestSubarrays xsxstoplamı artırarak ve ilk elemanı artırarak soldan sağa doğru sıralanan {en büyük toplam, en küçük ilk eleman} 'ın etkin sınırında olan alt diziler dizisidir .

Gitmek için bestSubarrays xsiçin bestSubarrays (x:xs), biz

  1. diziyi ilk elemanlardan küçük olan xbir sol tarafa ve ilk elemanlardan büyük olan bir sağ tarafa bölün x,
  2. xsol taraftaki en sağdaki alt diziye geçerek yeni bir alt dizi bulun ,
  3. alt dizilerin önekini yeni alt diziden daha küçük toplamla sağ taraftan bırakın,
  4. sol tarafı, yeni alt diziyi ve sağ tarafın kalanını birleştirin.

O(logn)


1

Bu cevap Ton Hospel'in cevabı üzerine genişliyor.

Sorun, yineleme kullanılarak dinamik programlama ile çözülebilir

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

burada giriş dizisidir ve indeksi ile biten herhangi bir azalan alt dizi maksimum değerine ulaşılabilir toplamdır . Gerçek çözelti daha sonra aşağıdaki pas kodunda olduğu gibi kullanılarak geri çekilebilir .(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()));
}

Çevrimiçi deneyin!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.