Hapus entri dari array untuk mengurutkannya dan memaksimalkan jumlah elemen

13

Tantangan ini adalah dari tes masuk ke kursus keamanan cyber nomor tertutup. Lagi pula itu tidak ada hubungannya dengan keamanan cyber, itu hanya untuk menguji para siswa logika dan keterampilan pengkodean.

Tugas

Tulis sebuah program yang menghapus entri dari array sehingga nilai-nilai yang tersisa diurutkan dalam urutan menurun ketat dan jumlah mereka adalah dimaksimalkan di antara semua kemungkinan urutan penurunan lainnya.

Masukan dan keluaran

Masukan akan menjadi sebuah array nilai integer ketat lebih besar dari 0dan semua berbeda satu sama lain . Anda bebas memilih apakah akan membaca input dari file, baris perintah atau stdin.

Output akan menjadi subarray descending- sort dari input yang jumlahnya lebih besar dari subarray descending-sort yang mungkin lainnya.

Catatan: [5, 4, 3, 2] adalah subarray [5, 4, 1, 3, 2], bahkan jika 4dan 3tidak berdekatan. Hanya karena 1sudah muncul.

Solusi bruteforce

Solusi paling sederhana tentu saja akan beralih di antara semua kombinasi yang mungkin dari array yang diberikan dan mencari yang diurutkan dengan jumlah terbesar, yaitu, dengan 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

Sayangnya, sejak memeriksa apakah array diurutkan, dan menghitung jumlah unsur itu adalah dan karena operasi ini akan dilakukan kali untuk dari ke , kompleksitas waktu asimtotik akan

Tantangan

Tujuan Anda adalah untuk mencapai kompleksitas waktu yang lebih baik daripada bruteforce di atas. Solusi dengan kompleksitas waktu asimptotik terkecil adalah pemenang tantangan. Jika dua solusi memiliki kompleksitas waktu asimptotik yang sama, pemenang akan menjadi satu dengan kompleksitas spasial asimptotik terkecil.

Catatan: Anda dapat mempertimbangkan membaca, menulis, dan membandingkan atom bahkan dalam jumlah besar.

Catatan: Jika ada dua atau lebih solusi mengembalikan salah satu dari mereka.

Uji kasus

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]
Marco
sumber
Terkait (Saya tidak dapat memeriksa sekarang apakah kedua algoritma itu sebenarnya setara, tapi saya pikir mereka mungkin.)
Martin Ender
Komentar bukan untuk diskusi panjang; percakapan ini telah dipindahkan ke obrolan .
Martin Ender

Jawaban:

3

Perl

Ini harus O (n ^ 2) dalam waktu dan O (n) dalam ruang

Berikan angka yang dipisahkan oleh spasi pada satu baris ke 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";
Ton Hospel
sumber
3

HAI(ncatatann)HAI(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

Bagaimana itu bekerja

bestSubarrays xsadalah urutan subarrays xsyang berada di perbatasan efisien {jumlah terbesar, elemen pertama terkecil}, dipesan dari kiri ke kanan dengan meningkatkan jumlah dan meningkatkan elemen pertama.

Untuk pergi dari bestSubarrays xske bestSubarrays (x:xs), kita

  1. pisahkan urutan menjadi sisi kiri dengan elemen pertama kurang dari x, dan sisi kanan dengan elemen pertama lebih besar dari x,
  2. temukan subarray baru dengan menambahkan xke subarray paling kanan di sisi kiri,
  3. letakkan awalan subarrays dari sisi kanan dengan jumlah yang lebih kecil dari subarray baru,
  4. menyatukan sisi kiri, subarray baru, dan sisanya dari sisi kanan.

HAI(catatann) waktu.

Anders Kaseorg
sumber
1

Jawaban ini berkembang pada jawaban Ton Hospel.

Masalahnya dapat diselesaikan dengan pemrograman dinamis menggunakan perulangan

T(saya)=Sebuahsaya+maks[{0}{T(j)|0j<sayaSebuahsayaSebuahj}]

dimana (Sebuahsaya) adalah urutan input dan T(saya) jumlah yang dapat dicapai secara maksimal dari setiap sub-urutan menurun yang diakhiri dengan indeks saya. Solusi aktual kemudian dapat ditelusuri menggunakanT, seperti pada kode karat berikut.

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()));
}

Cobalah online!

politza
sumber