Algorithmes avancés pour problèmes de programmation compétitive

A. Sous-tableau maximum à deux segments

Algorithme standard du sous-tableau maximum :

for (int idx = 1; idx <= n; idx++) 
    cumul[idx] = max(cumul[idx - 1] + valeurs[idx], valeurs[idx]);

cumul[i] représente la somme maximale se terminant à la position i, calculée à partir de la valeur précédente ou en recommençant une nouvelle séquence.

Solution pour deux segments non adjacents :

  • prefixMax[i] : somme maximale de 1 à i
  • suffixMax[i] : somme maximale de i à n
  • Mise à jour des tablaeux pour obtenir les sommes se terminant/débutant à chaque position
  • Résultat maximal de prefixMax[i] + suffixMax[i+1] pour chaque i
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
const int TAILLE = 100010;

int n, resultat;
int valeurs[TAILLE];
int prefixMax[TAILLE], suffixMax[TAILLE];

signed main() {
    cin >> n;
    for (int idx = 1; idx <= n; idx++) cin >> valeurs[idx];
    
    prefixMax[1] = valeurs[1];
    for (int idx = 2; idx <= n; idx++) 
        prefixMax[idx] = max(prefixMax[idx - 1] + valeurs[idx], valeurs[idx]);
    for (int idx = 2; idx <= n; idx++)
        prefixMax[idx] = max(prefixMax[idx - 1], prefixMax[idx]);
    
    suffixMax[n] = valeurs[n];
    for (int idx = n - 1; idx >= 1; idx--)
        suffixMax[idx] = max(suffixMax[idx + 1] + valeurs[idx], valeurs[idx]);
    for (int idx = n - 1; idx >= 1; idx--)
        suffixMax[idx] = max(suffixMax[idx + 1], suffixMax[idx]);
    
    resultat = prefixMax[1] + suffixMax[2];
    for (int sep = 2; sep <= n - 1; sep++)
        resultat = max(resultat, prefixMax[sep] + suffixMax[sep + 1]);
    
    cout << resultat << endl;
}

Problème original : P2642

B. Mise à jour d'intervalle avec séquence arithmétique

Ajout d'une progression arithmétique sur un entervalle [L,R] :

Index 1 2 3 4 5 6 7 8
Valeurs 0 S S+D S+2D S+3D E 0 0
Différence première 0 S D D D D -E 0
Différence seconde 0 S D-S 0 0 0 -E-D E

Opérations sur le tableau de différences secondes :

diff[L] += S;
diff[L+1] += D - S;
diff[R+1] -= (E + D);
diff[R+2] += E;
#include <iostream>
#define int long long
using namespace std;
const int MAX = 500010;

int n, m, res;
int diff[MAX];

signed main() {
    cin >> n >> m;
    while (m--) {
        int L, R, S, E;
        cin >> L >> R >> S >> E;
        int ecart = (E - S) / (R - L);
        diff[L] += S;
        diff[L+1] += ecart - S;
        diff[R+1] -= (E + ecart);
        diff[R+2] += E;
    }
    for (int idx = 1; idx <= n+1; idx++) diff[idx] += diff[idx-1];
    for (int idx = 1; idx <= n+1; idx++) diff[idx] += diff[idx-1];
    for (int idx = 1; idx <= n; idx++) res ^= diff[idx];
    cout << res << endl;
}

Problème original : P4231

C. Séquence adorable

État DP : dp[i][v][s][d]

  • i : position actuelle
  • v : valeur à la position i
  • s : somme des valeurs jusqu'à i
  • d : relasion avec élément précédent (0 : ≥, 1 : <)
for (int pos = 1; pos <= n; pos++) {
    int min_val = 0, max_val = 40;
    if (input[pos] != -1) min_val = max_val = input[pos];
    for (int val = min_val; val <= max_val; val++) {
        for (int somme = val*(pos-1); somme <= 40*(pos-1); somme++) {
            for (int prev = 0; prev <= val; prev++)
                dp[pos][val][val+somme][0] += dp[pos-1][prev][somme][0] + dp[pos-1][prev][somme][1];
            for (int prev = val+1; prev <= 40; prev++)
                dp[pos][val][val+somme][1] += dp[pos-1][prev][somme][0];
        }
    }
}
#include <iostream>
#define int long long
using namespace std;
const int MOD = 1e9+7;

int n, total;
int input[41];
int dp[41][41][1601][2];

signed main() {
    cin >> n;
    dp[0][0][0][0] = 1;
    for (int idx = 1; idx <= n; idx++) cin >> input[idx];
    
    for (int pos = 1; pos <= n; pos++) {
        int min_val = 0, max_val = 40;
        if (input[pos] != -1) min_val = max_val = input[pos];
        for (int val = min_val; val <= max_val; val++) {
            for (int somme = val*(pos-1); somme <= 40*(pos-1); somme++) {
                for (int prev_val = 0; prev_val <= val; prev_val++) {
                    dp[pos][val][val + somme][0] = 
                        (dp[pos][val][val + somme][0] + 
                         dp[pos-1][prev_val][somme][0] + 
                         dp[pos-1][prev_val][somme][1]) % MOD;
                }
                for (int prev_val = val+1; prev_val <= 40; prev_val++) {
                    dp[pos][val][val + somme][1] = 
                        (dp[pos][val][val + somme][1] + 
                         dp[pos-1][prev_val][somme][0]) % MOD;
                }
            }
        }
    }
    
    for (int val = 0; val <= 40; val++)
        for (int somme = 0; somme <= 40*n; somme++)
            total = (total + dp[n][val][somme][0] + dp[n][val][somme][1]) % MOD;
    
    cout << total << endl;
}

D. Jeu de pierres

Modélisation par coefficients binômiaux :

1 2 3 4 5 6
1 C(0,0) C(1,1) C(2,2) C(3,3) C(4,4) C(5,5)
2 C(1,0) C(2,1) C(3,2) C(4,3) C(5,4)

Solution :

#include <iostream>
#define int long long
using namespace std;
const int MAX = 1000010;
const int MOD = 1e9+7;

int n, res;
int fact[MAX], invFact[MAX];
int a[MAX];

int puissance(int base, int exp) {
    int result = 1;
    while (exp) {
        if (exp & 1) result = result * base % MOD;
        base = base * base % MOD;
        exp >>= 1;
    }
    return result;
}

int combinaison(int n, int k) {
    if (k < 0 || k > n) return 0;
    return fact[n] * invFact[k] % MOD * invFact[n - k] % MOD;
}

signed main() {
    cin >> n;
    n++;
    for (int idx = 1; idx <= n; idx++) cin >> a[idx];
    
    fact[0] = invFact[0] = 1;
    for (int i = 1; i < MAX; i++) {
        fact[i] = fact[i-1] * i % MOD;
        invFact[i] = puissance(fact[i], MOD-2);
    }
    
    for (int idx = 1; idx <= n; idx++) 
        res = (res + combinaison(a[idx] + idx - 1, a[idx] - 1)) % MOD;
    
    cout << res << endl;
}

Problème original : Placing Jinas

Étiquettes: sous-tableau_maximum mise_à_jour_intervalle programmation_dynamique coefficients_binomiaux

Publié le 8 juillet à 20h47