Soluções para Problemas do KEYENCE Programming Contest 2021

C - Robô em Grade

Dada uma grade de H linhas e W colunas, com k células pré-definidas como 'R', 'D' ou 'X'. 'R' só permite movimento para a direita, 'D' só para baixo, e 'X' permite ambos os movimentos. As células restantes podem ser preenchidas com qualquer um dos três símbolos. Para cada possível preenchimento, calcule a soma dos números de caminhos de (1,1) a (H,W) módulo 998244353.

A solução envolve calcular a acessibilidade de cada célula. Para células definidas, o valor é 0 ou 1; para indefinidas, é 2/3. Multiplicando pelo número de formas de preencher as células indefinidas e utilizando inversos modulaers, podemos reoslver o problema em tempo eficiente.

// Código reescrito para ilustração
const int MAX_DIM = 5e3 + 10;
const int MOD = 998244353;
int grade[MAX_DIM][MAX_DIM], linhas, colunas, definidas;
int dp[MAX_DIM][MAX_DIM];
char entrada[5];

int exponenciacao_modular(int base, int expoente) {
    int resultado = 1;
    for (; expoente; expoente >>= 1, base = 1LL * base * base % MOD)
        if (expoente & 1) resultado = 1LL * resultado * base % MOD;
    return resultado;
}

void atualizar(int &destino, int valor) {
    destino = (1LL * destino + valor > MOD) ? destino + valor - MOD : destino + valor;
}

int main() {
    linhas = read(); colunas = read(); definidas = read();
    int i, j, x, y;
    for (i = 1; i <= linhas; i++)
        for (j = 1; j <= colunas; j++)
            grade[i][j] = -1;
    for (i = 1; i <= definidas; i++) {
        x = read(); y = read(); scanf("%s", entrada + 1);
        if (entrada[1] == 'X') grade[x][y] = 0;
        else if (entrada[1] == 'R') grade[x][y] = 1;
        else grade[x][y] = 2;
    }
    memset(dp, 0, sizeof(dp));
    dp[1][1] = 1;
    int probabilidade = 1LL * exponenciacao_modular(3, MOD - 2) * 2 % MOD;
    for (i = 1; i <= linhas; i++) {
        for (j = 1; j <= colunas; j++) {
            if (grade[i][j] == 2) atualizar(dp[i + 1][j], dp[i][j]);
            else if (grade[i][j] == 1) atualizar(dp[i][j + 1], dp[i][j]);
            else if (grade[i][j] == 0) atualizar(dp[i + 1][j], dp[i][j]), atualizar(dp[i][j + 1], dp[i][j]);
            else {
                atualizar(dp[i + 1][j], 1LL * dp[i][j] * probabilidade % MOD);
                atualizar(dp[i][j + 1], 1LL * dp[i][j] * probabilidade % MOD);
            }
        }
    }
    dp[linhas][colunas] = 1LL * dp[linhas][colunas] * exponenciacao_modular(3, linhas * colunas - definidas) % MOD;
    printf("%d\n", dp[linhas][colunas]);
    return 0;
}

D - Escolhendo Lados

Existem 2^N jogadores. Use o menor número possível de operações, onde cada operação divide todos em duas equipes, de modo que:

  • Exista um inteiro n tal que para cada par 1 ≤ i < j ≤ 2^N, i e j estejam na mesma equipe exatamente n vezes.
  • Exista um inteiro m tal que para cada par 1 ≤ i < j ≤ 2^N, i e j estejam em equipes diferentes exatamente m vezes.

Dado 1 ≤ N ≤ 8, forneça uma solução.

Conclusão: A resposta é 2^N - 1. A prova envolve contagem de pares e razões entre n e m.

Construção: Na i-ésima rodada, para cada j, se a contagem de bits 1 em (i AND j) for par, j vai para a equipe A; caso contrário, para B.

// Código reescrito para ilustração
#include <iostream>
using namespace std;

int contar_bits(int valor) {
    int contagem = 0;
    for (; valor; valor -= (valor & -valor)) contagem++;
    return contagem;
}

int main() {
    int n;
    cin >> n;
    int total_jogadores = 1 << n;
    cout << total_jogadores - 1 << endl;
    for (int rodada = 1; rodada < total_jogadores; rodada++) {
        for (int jogador = 0; jogador < total_jogadores; jogador++) {
            if (contar_bits(rodada & jogador) % 2 == 1) cout << "B";
            else cout << "A";
        }
        cout << endl;
    }
    return 0;
}

E - Formiga Gananciosa

No eixo numérico, existem N doces, com o i-ésimo na posição 2i e valor a_i. Inicialmente, a Formiga escolhe uma posição entre 1, 3, ..., 2N+1.

Snuke joga primeiro e pode remover qualquer doce. A Formiga, em cada turno, escolhe remover o doce com maior valor entre os dois mais próximos à esquerda e à direita.

Para cada posição inicial da Formiga, determine o valor máximo que Snuke pode obter.

A solução usa programação dinâmica com estado f[l][r][k], representando o intervalo (l, r) ainda não removido e o número k de turnos restantes para Snuke.

// Código reescrito para ilustração
#include <iostream>
#include <cstring>
using namespace std;

const int MAX_N = 410;
int n, valores[MAX_N];
int dp[MAX_N][MAX_N][MAX_N];

void maximizar(int &destino, int candidato) {
    if (candidato > destino) destino = candidato;
}

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> valores[i];
    memset(dp, -1, sizeof(dp));
    dp[0][n + 1][(n + 1) / 2] = 0;
    for (int comprimento = n + 2; comprimento >= 3; comprimento--) {
        for (int esq = 0; esq + comprimento - 1 <= n + 1; esq++) {
            int dir = esq + comprimento - 1;
            for (int turnos = 0; turnos <= (n + 1) / 2; turnos++) {
                int valor_atual = dp[esq][dir][turnos];
                if (valor_atual < 0) continue;
                if (2 * (turnos - 1) < comprimento - 1) {
                    maximizar(dp[esq + 1][dir][turnos - 1], valor_atual + valores[esq + 1]);
                    maximizar(dp[esq][dir - 1][turnos - 1], valor_atual + valores[dir - 1]);
                }
                if (2 * turnos < comprimento - 1) {
                    if (valores[esq + 1] > valores[dir]) maximizar(dp[esq + 1][dir][turnos], valor_atual);
                    if (valores[dir - 1] > valores[esq]) maximizar(dp[esq][dir - 1][turnos], valor_atual);
                }
            }
        }
    }
    for (int posicao = 0; posicao <= n; posicao++) cout << dp[posicao][posicao + 1][0] << endl;
    return 0;
}

Tags: dynamic-programming modular-arithmetic bitmask game-theory

Publicado em 6-5 01:03 por Thomas