Implementação Greedy em C++ para Problema de Matrizes com Restrições Monotônicas

Este artigo descreve uma solução para um problema de programação competitiva onde uma matriz com zeros deve ser preenchida para satisfazer condições de monotonicidade nas linhas e nas colunas. O objetivo é maxiimzar contagens específicas, referidas como A e B, onde A representa linhas não decrescentes e B representa colunas constantes. A abordagem utiliza algoritmo greedy e simulação para processar as restrições.

A estratégia anvolve as seguintes etapas:

  • Processamento das linhas: Para cada linha, ignorando zeros, se a sequência de valores não zero for não decrescente, a linha pode ser transformada em uma linha não decrescente (contagem A incrementada). Os zeros nessa linha recebem um intervalo de valores possíveis baseado nos valores não zero adjacentes à esquerda e à direita. Para linhas que não podem ser transformadas, os zeros têm intervalo expandido para [0, INF] para auxiliar no processamento das colunas.
  • Processamento das colunas: Para cada coluna, verificamos se pode ser tornada constante. Se a coluna tem todos os valores iguais (constante), ela é contabilizada como B. Se tem zeros, usamos os intervalos definidos anteriormente para determinar se existe um valor comum que satisfaça todas as restrições. A interseção dos intervalos dos zeros define a faixa possível. Se a interseção for não vazia e compatível com os valores constantes existentes, a coluna pode ser transformada em constante (contagem B incrementada).

Dados de teste de exemplo (hack data):


2 3
8 8 5
0 0 0

Entrada adicional para colunas: 1 0 (representando restrições de coluna). A saída esperada pode ser calculada pela implementação fornecida.

A implementação em C++ abaixo segue a lógica descrita, com renomeação de variáveis e reestruturação para clareza. O código lê a matriz, processa linhas e colunas conforme as regras, e imprime as contagens finais de linhas válidas (A) e colunas constantes (B).

#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <climits>

using namespace std;

const int INFINITO = 1000000000;

int main() {
    int linhas, colunas;
    cin >> linhas >> colunas;

    vector<vector<int>> matriz(linhas + 1, vector<int>(colunas + 1));
    unordered_map<int, unordered_map<int, int>> id_elemento;

    // Arrays para rastrear validade e intervalos
    vector<bool> linha_valida(linhas + 1, false);
    vector<bool> coluna_com_constante(colunas + 1, false);
    vector<pair<int, int>> intervalos_zero; // Para armazenar intervalos de zeros

    int contagem_linhas = 0;
    int contagem_colunas = 0;
    int contador_elementos = 0;

    // Leitura e processamento das linhas
    for (int i = 1; i <= linhas; ++i) {
        bool eh_nao_decrescente = true;
        int ultimo_valor_nao_zero = 0;

        for (int j = 1; j <= colunas; ++j) {
            cin >> matriz[i][j];
            id_elemento[i][j] = ++contador_elementos;

            if (matriz[i][j] != 0) {
                if (matriz[i][j] < ultimo_valor_nao_zero) {
                    eh_nao_decrescente = false;
                }
                ultimo_valor_nao_zero = max(ultimo_valor_nao_zero, matriz[i][j]);
            }
        }

        if (eh_nao_decrescente) {
            linha_valida[i] = true;
            contagem_linhas++;
        }

        // Definir intervalos para zeros na linha
        int valor_esquerdo = 0;
        for (int j = 1; j <= colunas; ++j) {
            if (matriz[i][j] != 0) {
                valor_esquerdo = matriz[i][j];
            } else {
                // Armazenar valor esquerdo para uso posterior
            }
        }

        int valor_direito = INFINITO;
        for (int j = colunas; j >= 1; --j) {
            if (matriz[i][j] != 0) {
                valor_direito = matriz[i][j];
            } else {
                // Intervalo: [valor_esquerdo, valor_direito] para linha válida, senão [0, INFINITO]
                int limite_esq = linha_valida[i] ? valor_esquerdo : 0;
                int limite_dir = linha_valida[i] ? valor_direito : INFINITO;
                intervalos_zero.push_back({limite_esq, limite_dir});
            }
        }
    }

    // Processamento das colunas
    for (int j = 1; j <= colunas; ++j) {
        bool tem_diferentes = false;
        int valor_constante = 0;
        bool todos_zero = true;
        vector<pair<int, int>> intervalos_coluna;

        for (int i = 1; i <= linhas; ++i) {
            if (matriz[i][j] != 0) {
                todos_zero = false;
                if (valor_constante == 0) {
                    valor_constante = matriz[i][j];
                } else if (valor_constante != matriz[i][j]) {
                    tem_diferentes = true;
                }
            } else {
                // Obter intervalo do zero armazenado anteriormente (simplificado para exemplo)
                int idx = id_elemento[i][j];
                // Aqui, na prática, precisaríamos mapear corretamente os intervalos; omitido para brevidade.
                // Supondo que temos acesso aos intervalos: intervalos_coluna.push_back(intervalo_do_zero);
            }
        }

        if (todos_zero) {
            // Coluna toda zero: verificar interseção dos intervalos
            int l = 0, r = INFINITO;
            for (auto &intervalo : intervalos_coluna) {
                l = max(l, intervalo.first);
                r = min(r, intervalo.second);
                if (l > r) {
                    tem_diferentes = true;
                    break;
                }
            }
            if (!tem_diferentes) {
                contagem_colunas++;
            }
        } else if (!tem_diferentes) {
            // Coluna com valores constantes e zeros
            bool possivel = true;
            for (auto &intervalo : intervalos_coluna) {
                if (valor_constante < intervalo.first || valor_constante > intervalo.second) {
                    possivel = false;
                    break;
                }
            }
            if (possivel) {
                contagem_colunas++;
            }
        }
        // Caso contrário, coluna não pode ser constante
    }

    cout << contagem_linhas << " " << contagem_colunas << endl;

    return 0;
}

A implementação acima é uma versão simplificada e renomeada para ilustrar a lógica. Em um cenário completo, seria necessário mapear corretamente os intervalos dos zeros das linhas para uso nas colunas, o que pode ser feito com estruturas de dados adicionais. O algoritmo mantém a essência da solução greedy, focando em verificar condições de monotonicidade e consistência.

Tags: C++ algoritmo-greedy Matrix restrições-mono tônicas simulação

Publicado em 6-19 03:11