Xóa các mục từ mảng để sắp xếp nó và tối đa hóa tổng các phần tử


13

Thử thách này là từ một bài kiểm tra nhập học đến một khóa học an ninh mạng số kín. Dù sao, nó không liên quan đến an ninh mạng, nó chỉ để kiểm tra các kỹ năng mã hóa và logic của sinh viên.

Bài tập

Viết chương trình loại bỏ các mục từ một mảng để các giá trị còn lại được sắp xếp theo thứ tự giảm nghiêm ngặt và tổng của chúng là tối đa trong số tất cả các chuỗi giảm có thể khác.

Đầu vào và đầu ra

Đầu vào sẽ là một mảng các giá trị nguyên lớn hơn 0và hoàn toàn khác nhau . Bạn có thể tự do chọn đọc đầu vào từ tệp, dòng lệnh hoặc stdin.

Đầu ra sẽ là một phân đoạn được sắp xếp giảm dần của một đầu vào, có tổng lớn hơn bất kỳ phân đoạn giảm dần được sắp xếp có thể khác.

Lưu ý: [5, 4, 3, 2] là một phân khúc của [5, 4, 1, 3, 2], ngay cả khi 43không liền kề. Chỉ vì 1đã được bật lên.

Dung dịch bruteforce

Tất nhiên, giải pháp đơn giản nhất sẽ được lặp lại trong số tất cả các kết hợp có thể có của mảng đã cho và tìm kiếm một thứ tự được sắp xếp với tổng lớn nhất, đó sẽ là, trong 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

Thật không may, vì kiểm tra xem mảng có được sắp xếp hay không và tính tổng của các phần tử của nó và vì thao tác này sẽ được thực hiện theo thời gian từ đến , độ phức tạp thời gian tiệm cận sẽ là

Thử thách

Mục tiêu của bạn là đạt được độ phức tạp thời gian tốt hơn so với bruteforce ở trên. Giải pháp với độ phức tạp thời gian tiệm cận nhỏ nhất là người chiến thắng thử thách. Nếu hai giải pháp có độ phức tạp thời gian tiệm cận giống nhau, người chiến thắng sẽ là người có độ phức tạp không gian tiệm cận nhỏ nhất.

Lưu ý: Bạn có thể xem xét việc đọc, viết và so sánh nguyên tử ngay cả trên số lượng lớn.

Lưu ý: Nếu có hai hoặc nhiều giải pháp trả về một trong hai giải pháp.

Các trường hợp thử nghiệm

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ên quan. (Tôi không thể kiểm tra ngay bây giờ liệu hai thuật toán trên thực tế có tương đương nhau không, nhưng tôi nghĩ chúng có thể giống nhau.)
Martin Ender

Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Martin Ender

Câu trả lời:


3

Perl

Đây phải là O (n ^ 2) trong thời gian và O (n) trong không gian

Đưa các số cách nhau bởi khoảng trắng trên một dòng cho 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

Ôi(nđăng nhậpn)Ôi(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

Làm thế nào nó hoạt động

bestSubarrays xslà chuỗi các tập hợp con xsnằm trên biên giới hiệu quả của {tổng lớn nhất, phần tử đầu tiên nhỏ nhất}, được sắp xếp từ trái sang phải bằng cách tăng tổng và tăng phần tử đầu tiên.

Để đi từ bestSubarrays xsđến bestSubarrays (x:xs), chúng tôi

  1. chia chuỗi thành một bên trái với các phần tử đầu tiên nhỏ hơn xvà một bên phải có các phần tử đầu tiên lớn hơn x,
  2. tìm một phân khúc mới bằng cách thêm vào xphân đoạn ngoài cùng bên phải ở bên trái,
  3. thả tiền tố của các phân đoạn từ bên phải với tổng nhỏ hơn so với phân đoạn mới,
  4. nối phần bên trái, phần phụ mới và phần còn lại của phần bên phải.

Ôi(đăng nhậpn)


1

Câu trả lời này mở rộng trên câu trả lời của TonMedel.

Vấn đề có thể được giải quyết bằng lập trình động bằng cách sử dụng phép lặp

T(Tôi)= =mộtTôi+tối đa[{0}{T(j)|0j<TôimộtTôimộtj}]

(mộtTôi)T(Tôi)Tô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()));
}

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.