web-dev-qa-db-fra.com

Somme maximale de sous-matrice modulo M

La plupart d’entre nous connaissons le problème de la sous-matrice maximum sum . Je suis tombé sur une variante de ce problème qui demande au programmeur d'émettre le maximum de toutes les sommes de sous-tableau modulo un nombre M. 

L’approche naïve pour résoudre cette variante consisterait à rechercher toutes les sommes possibles dans les sous-matrices (qui seraient de l’ordre de N ^ 2 où N est la taille du tableau). Bien sûr, cela ne suffit pas. La question est: comment pouvons-nous faire mieux? 

Exemple: Considérons le tableau suivant:

6 6 11 15 12 1 

Soit M = 13. Dans ce cas, la sous-matrice 6 6 (ou 12 ou 6 6 11 15 ou 11 15 12) donnera la somme maximale (= 12). 

26
Bhoot

SoitAnotre tableau d’entrée avec indexation basée sur zéro. Nous pouvons réduireAmoduloMsans changer le résultat.

Tout d’abord, réduisons le problème à un problème un peu plus facile en calculant un tableauPreprésentant les sommes de préfixe deA, moduloM:

A = 6 6 11 2 12 1
P = 6 12 10 12 11 12

Traitons maintenant les limites gauches possibles de nos sous-réseaux de solutions par ordre décroissant. Cela signifie que nous allons d’abord déterminer la solution optimale qui commence par l’index n - 1 , puis celle qui commence par l’index n - 2 etc.

Dans notre exemple, si nous choisissons i = 3 comme bordure gauche, les sommes possibles pour les sous-tableaux sont représentées par le suffixe P [3..n-1] plus une constante a = A [ i] - P [i] :

a = A[3] - P[3] = 2 - 12 = 3 (mod 13)
P + a = * * * 2 1 2

Le maximum global se produira également à un moment donné. Comme nous pouvons insérer les valeurs de suffixe de droite à gauche, nous avons maintenant réduit le problème à ce qui suit:

Étant donné un ensemble de valeursSet des entiers x etM, trouver le maximum de S + x moduloM

Celui-ci est simple: utilisez simplement un arbre de recherche binaire équilibré pour gérer les éléments deS. Étant donné une requête x , nous voulons trouver la plus grande valeur dansSqui est inférieure à M - x (c'est le cas où aucun débordement ne se produit lors de l'ajout de x ). S'il n'y a pas de telle valeur, utilisez simplement la plus grande valeur deS. Les deux peuvent être effectués en temps O (log | S |).

Durée totale d'exécution de cette solution: O (n log n)

Voici du code C++ pour calculer la somme maximale. Il faudrait quelques adaptations mineures pour renvoyer également les limites du sous-réseau optimal:

#include <bits/stdc++.h>
using namespace std;

int max_mod_sum(const vector<int>& A, int M) {
    vector<int> P(A.size());
    for (int i = 0; i < A.size(); ++i)
        P[i] = (A[i] + (i > 0 ? P[i-1] : 0)) % M;
    set<int> S;
    int res = 0;
    for (int i = A.size() - 1; i >= 0; --i) {
        S.insert(P[i]);
        int a = (A[i] - P[i] + M) % M;
        auto it = S.lower_bound(M - a);
        if (it != begin(S))
            res = max(res, *prev(it) + a);
        res = max(res, (*prev(end(S)) + a) % M);
    }
    return res;
}

int main() {
    // random testing to the rescue
    for (int i = 0; i < 1000; ++i) {
        int M = Rand() % 1000 + 1, n = Rand() % 1000 + 1;
        vector<int> A(n);
        for (int i = 0; i< n; ++i)
            A[i] = Rand() % M;
        int should_be = 0;
        for (int i = 0; i < n; ++i) {
            int sum = 0;
            for (int j = i; j < n; ++j) {
                sum = (sum + A[j]) % M;
                should_be = max(should_be, sum);
            }
        }
        assert(should_be == max_mod_sum(A, M));
    }
}
7
Niklas B.

Voici le code Java pour la somme maximale modulo du sous-tableau. Nous traitons le cas où nous ne pouvons pas trouver le moindre élément dans l’arbre strictement supérieur à s [i]

public static long maxModulo(long[] a, final long k) {
    long[] s = new long[a.length];
    TreeSet<Long> tree = new TreeSet<>();

    s[0] = a[0] % k;
    tree.add(s[0]);
    long result = s[0];

    for (int i = 1; i < a.length; i++) {

        s[i] = (s[i - 1] + a[i]) % k;

        // find least element in the tree strictly greater than s[i]
        Long v = tree.higher(s[i]);

        if (v == null) {
            // can't find v, then compare v and s[i]
            result = Math.max(s[i], result);
        } else {
            result = Math.max((s[i] - v + k) % k, result);
        }
        tree.add(s[i]);
    }
    return result;
 }
2
The Tran

Mise en œuvre totale de Java avec O (n * log (n))

import Java.io.BufferedReader;
import Java.io.InputStreamReader;
import Java.util.TreeSet;
import Java.util.stream.Stream;

public class MaximizeSumMod {

    public static void main(String[] args) throws Exception{

        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        Long times = Long.valueOf(in.readLine());

        while(times --> 0){
            long[] pair = Stream.of(in.readLine().split(" ")).mapToLong(Long::parseLong).toArray();
            long mod = pair[1];            
            long[] numbers = Stream.of(in.readLine().split(" ")).mapToLong(Long::parseLong).toArray();
            printMaxMod(numbers,mod);
        }
    }

    private static void printMaxMod(long[] numbers, Long mod) {

        Long maxSoFar = (numbers[numbers.length-1] + numbers[numbers.length-2])%mod;
        maxSoFar = (maxSoFar > (numbers[0]%mod)) ? maxSoFar : numbers[0]%mod;
        numbers[0] %=mod;
        for (Long i = 1L; i < numbers.length; i++) {
            long currentNumber = numbers[i.intValue()]%mod;            
            maxSoFar = maxSoFar > currentNumber ? maxSoFar : currentNumber;
            numbers[i.intValue()] = (currentNumber + numbers[i.intValue()-1])%mod;
            maxSoFar = maxSoFar > numbers[i.intValue()] ? maxSoFar : numbers[i.intValue()];
        }

        if(mod.equals(maxSoFar+1) || numbers.length == 2){
            System.out.println(maxSoFar);
            return;
        }

        long previousNumber = numbers[0];
        TreeSet<Long> set = new TreeSet<>();
        set.add(previousNumber);

        for (Long i = 2L; i < numbers.length; i++) {
            Long currentNumber = numbers[i.intValue()];
            Long ceiling = set.ceiling(currentNumber);
            if(ceiling == null){
                set.add(numbers[i.intValue()-1]);            
                continue;
            }

            if(ceiling.equals(currentNumber)){
                set.remove(ceiling);
                Long greaterCeiling = set.ceiling(currentNumber);
                if(greaterCeiling == null){
                    set.add(ceiling);
                    set.add(numbers[i.intValue()-1]);            
                    continue;
                }
                set.add(ceiling);                    
                ceiling = greaterCeiling;
            }
            Long newMax = (currentNumber - ceiling + mod);
            maxSoFar = maxSoFar > newMax ? maxSoFar :newMax;
            set.add(numbers[i.intValue()-1]);            
        }

        System.out.println(maxSoFar);

    }

}
1
fatih tekin

Pour moi, toutes les explications ici étaient terribles, car je n'ai pas eu la partie recherche/tri. Comment pouvons-nous rechercher/trier, n'était pas clair.

Nous savons tous que nous devons construire prefixSum, ce qui signifie sum of all elems from 0 to i with modulo m

Je suppose que ce que nous recherchons est clair… .. Sachant que subarray[i][j] = (prefix[i] - prefix[j] + m) % m (indiquant la somme modulo de l'index i à j), notre maximum lorsque préfixe donné [i] est toujours ce préfixe [j] qui est aussi proche que possible préfixe [i], mais légèrement plus grand.

Par exemple. pour m = 8, le préfixe [i] étant 5, nous recherchons la valeur suivante après 5, qui est dans notre prefixArray.

Pour une recherche efficace (recherche binaire), nous trions les préfixes.

Ce que nous ne pouvons pas faire, c’est d’abord construire le préfixeSum, puis itérer à nouveau de 0 à n et rechercher l’index dans le tableau de préfixes trié, car nous pouvons trouver et endIndex qui est plus petit que notre startIndex, ce qui n’est pas bon.

Par conséquent, ce que nous faisons est que nous itérons de 0 à n en indiquant le endIndex de notre somme de sous-tableau max potentielle, puis nous regardons dans notre tableau de préfixes triés (vide au début) qui contient les préfixes triés entre 0 et endIndex. .

def maximumSum(coll, m):
    n = len(coll)
    maxSum, prefixSum = 0, 0
    sortedPrefixes = []

    for endIndex in range(n):
        prefixSum = (prefixSum + coll[endIndex]) % m
        maxSum = max(maxSum, prefixSum)

        startIndex = bisect.bisect_right(sortedPrefixes, prefixSum)
        if startIndex < len(sortedPrefixes): 
            maxSum = max(maxSum, prefixSum - sortedPrefixes[startIndex] + m)

        bisect.insort(sortedPrefixes, prefixSum)

    return maxSum
1
denis631

Ajouter du code STL C++ 11 basé sur la solution proposée par @Pham Trung. Ça pourrait être pratique. 

#include <iostream>
#include <set>

int main() {
    int N;
    std::cin>>N;
    for (int nn=0;nn<N;nn++){
        long long n,m;
        std::set<long long> mSet;
        long long maxVal = 0; //positive input values
        long long sumVal = 0;

        std::cin>>n>>m;
        mSet.insert(m);
        for (long long q=0;q<n;q++){
            long long tmp;

            std::cin>>tmp;
            sumVal = (sumVal + tmp)%m;
            auto itSub = mSet.upper_bound(sumVal);
            maxVal = std::max(maxVal,(m + sumVal - *itSub)%m);
            mSet.insert(sumVal);                
        }
        std::cout<<maxVal<<"\n";
    }
}
1
Nir