Solução para o Problema de Lógica de Três Valores do NOIP 2023

Descrição do Problema

Em lógica de três valores, uma variável pode assumir os valores Verdadeiro (T), Falso (F) ou Desconhecido (U). A operação lógica de negação é definida como: ¬T = F, ¬F = T, ¬U = U.

Dado n variáveis x1, ..., xn, existem m instruções executadas sequencialmente, de três tipos possíveis: atribuição direta de T, F ou U; atribuição de outra variável; ou atribuição da negação de outra variável. O objetivo é encontrar a atribuição inicial que minimize o número de variáveis U, garantindo que após executar todas as instruções, os valores finais sejam iguais aos iniciais. É garantido que pelo menos uma solução válida existe.

Formato de Entrada

A entrada contém múltiplos casos de teste. A primeira linha tem dois inteiros c e t, indicando o identificador do teste e o número de casos. Para cada caso:

  • Primeira linha: inteiros n e m, representando o número de variáveis e instruções.
  • m linhas seguintes: cada instrução começa com um caractere v entre 'T', 'F', 'U', '+' ou '-'. Se v for 'T', 'F' ou 'U', segue-se um inteiro i, indicando xi ← v. Se v for '+', seguem-se inteiros i e j, indicando xi ← xj. Se v for '-', seguem-se inteiros i e j, indicando xi ← ¬xj.

Formato de Saída

Para cada caso de teste, imprima um único inteiro: o número mínimo de variáveis U em uma solução válida.

Exemplo

Entrada de teste 1:

1 3
3 3
- 2 1
- 3 2
+ 1 3
3 3
- 2 1
- 3 2
- 1 3
2 2
T 2
U 2

Saída esperada:

0
3
1

Explicação do Exemplo

No primeiro caso, as instruções são x2 ← ¬x1, x3 ← ¬x2, x1 ← x3. Uma solução viável é x1=T, x2=F, x3=T, com 0 variáveis U.

No segundo caso, as instruções são x2 ← ¬x1, x3 ← ¬x2, x1 ← ¬x3. A única solução é x1=x2=x3=U, resultando em 3 variáveis U.

No terceiro caso, as instruções são x2 ← T e x2 ← U. Uma solução ótima é x1=T, x2=U, com 1 variável U.

Restrições

Para todos os testes: 1 ≤ t ≤ 6, 1 ≤ n, m ≤ 10^5. Os testes são categorizados conforme a tabela abaixo:

Subtarefa n, m ≤ Valores possíveis de v
1,2 10 T F U + -
3 10^3 T F U
4 10^5 T F U
5 10^3 U +
6 10^5 U +
7 10^3 + -
8 10^5 + -
9 10^3 T F U + -
10 10^5 T F U + -

Abordagem

O problema pode ser modelado utilizando uma estrutura de dados Union-Find (união-busca) adaptada para lidar com os três valores lógicos. As variáveis são tratadas como nós em um grafo, onde as instruções definem relações de igualdade ou negação. A ideia é determinar componentes conectados e verificar a consistência das atribuições, minimizando ocorrências de U.

Implementação

Abaixo está uma implementação em C++ que utiliza Union-Find com caminhamento otimizado. As variáveis são representadas por índices, e operações especiais são usadas para representar T, F e U.

#include <iostream>
#include <vector>
using namespace std;

const int MAX_NODES = 100005;
const int TRUE_VAL = 100001;   // Representa T
const int FALSE_VAL = -100001; // Representa F
const int UNK_VAL = 0;         // Representa U

int parent[MAX_NODES];
bool visited[300005];

int find(int x) {
    if (x == TRUE_VAL || x == FALSE_VAL) return x;
    if (x == UNK_VAL || visited[x + MAX_NODES]) return UNK_VAL;
    if (visited[-x + MAX_NODES]) return TRUE_VAL; // Lógica para negação
    if (x < 0) {
        if (x == -parent[-x]) return x;
        visited[x + MAX_NODES] = true;
        int res = find(-parent[-x]);
        visited[x + MAX_NODES] = false;
        return res;
    }
    if (x == parent[x]) return x;
    visited[x + MAX_NODES] = true;
    int res = find(parent[x]);
    parent[x] = res;
    visited[x + MAX_NODES] = false;
    return res;
}

int main() {
    int c, t;
    cin >> c >> t;
    while (t--) {
        int n, m;
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) parent[i] = i;
        for (int i = 0; i < m; ++i) {
            char op;
            int a, b;
            cin >> op;
            if (op == 'T') {
                cin >> a;
                parent[a] = TRUE_VAL;
            } else if (op == 'F') {
                cin >> a;
                parent[a] = FALSE_VAL;
            } else if (op == 'U') {
                cin >> a;
                parent[a] = UNK_VAL;
            } else if (op == '+') {
                cin >> a >> b;
                parent[a] = parent[b];
            } else { // op == '-'
                cin >> a >> b;
                parent[a] = -parent[b];
            }
        }
        int unknownCount = 0;
        for (int i = 1; i <= n; ++i) {
            if (find(i) == UNK_VAL) unknownCount++;
        }
        cout << unknownCount << endl;
    }
    return 0;
}

Tags: lógica de três valores Union-Find NOIP Grafos algoritmo de busca

Publicado em 6-7 05:04 por Thomas