" Competição de Algoritmos: 300 Perguntas Rápidas " será publicado em 2024 e é um caderno de exercícios auxiliar para "Competição de Algoritmos" .
Todas as perguntas são colocadas no OJ New Online Judge , criado por você mesmo .
Os códigos são fornecidos em três linguagens: C/C++, Java e Python. Os tópicos são principalmente de nível médio a baixo e são adequados para alunos iniciantes e avançados.
Diretório de artigos
" Jogo de iluminação de lâmpadas ", link: http://oj.ecustacm.cn/problem.php?id=1134
Descrição da pergunta
[Descrição do problema] Existe uma matriz de lâmpada n*n, b significa que a luz está escura e w significa que a luz está acesa. Cada lâmpada possui um botão que a controla. Quando um botão é pressionado, o botão e as luzes ao seu redor (cima, baixo, esquerda e direita) mudam de estado (claro -> escuro, escuro -> claro). O número mínimo de botões a serem pressionados para ligar ou desligar todas as luzes.
[Formato de entrada] Insira vários conjuntos de dados. A primeira linha de cada conjunto de dados possui um número inteiro n (1≤n≤10), e as próximas n linhas possuem n caracteres em cada linha representando a matriz inicial da lâmpada.
[Formato de saída] Se todas as lâmpadas puderem ficar claras ou escuras, produza o número mínimo de botões pressionados. Se não puder ser alcançado, imprima "Impossível" (sem aspas).
【Amostra de entrada】
4
bwwb
bbwb
bwwb
bwww
4
bwbw
wwww
bbwb
bwwb
【Amostra de saída】
4
Impossible
responder
O “Jogo Lamplight” é um problema clássico com muitas soluções.
Se não há limite de “número mínimo de botões” e só é necessário para atingir a escuridão total ou a extinção total, como operar? Aqui apresentamos um método denominado "método exaustivo de primeira linha", que é simples e fácil de implementar. Suponha que o objetivo é deixar todas as luzes pretas (escuras). A operação é a seguinte:
(1) Não pressione o botão da primeira linha, apenas encontre a posição da luz branca;
(2) A segunda linha corresponde para a posição da luz branca na primeira linha. , são todos botões. Quando pressionados, as luzes na parte superior, inferior, esquerda e direita mudarão de cor, especialmente as luzes brancas na linha anterior ficarão pretas, garantindo que o a primeira linha fica preta; (3) Os botões em cada linha correspondem à linha anterior
. A luz branca torna toda a linha anterior preta.
Esta questão requer o "número mínimo de botões".Você pode tentar todos os botões para encontrar o menor. Para reduzir o número de tentativas, você pode combinar os métodos acima. Para esta questão, n≤10, a escala é muito pequena, então este método violento também é viável.
Supondo que o objetivo é deixar tudo preto no final, comece na primeira linha e pressione os botões linha por linha.
A primeira linha tem um total de 16 maneiras de pressionar o botão de 0000 a 1111. 0000 significa não pressionar nenhum botão, 0001 significa pressionar apenas o 1º botão, 0010 significa pressionar apenas o 2º botão, 0011 significa pressionar o 1º e 2º botões,..., etc.
Após selecionar um método na primeira linha, continue pressionando na segunda linha. Como pressionar a segunda linha? Obviamente você deve ter certeza de que a primeira linha fique preta. Então a posição do botão na segunda linha deve ser igual à posição do branco na primeira linha, porque depois que o botão na segunda linha for pressionado, o branco nele ficará preto.
Continue pressionando as seguintes linhas de acordo com as regras acima até o final.
Aqui estão alguns exemplos. O "estado inicial" na imagem é o primeiro exemplo de lâmpada. Preto significa escuro e branco significa claro. As coordenadas do canto superior esquerdo são (0,0) e as coordenadas do canto inferior direito são (3,3).
(1) Configurações dos botões na primeira linha. Tome como exemplo pressionar 0011 na primeira linha, o que significa pressionar o 1º e o 2º botões. Após pressionar o primeiro botão (0,0), você obtém a imagem (1), e após pressionar o segundo botão (0,1), você obtém a imagem (2). Grave as coordenadas (0,0) e (0,1) dos dois botões.
(2) Configurações dos botões na segunda linha. Neste momento, apenas a segunda luz da primeira linha é branca e precisa ser transformada em preta. Em seguida, o segundo botão deve ser pressionado na segunda linha para fazer com que a luz branca acima se transforme em luz negra. As coordenadas dos botões que precisam ser pressionados na segunda linha são (1,1).O resultado da Figura (3) é obtido.A primeira linha é completamente preta.
(3) Configurações dos botões na terceira linha. Como a segunda linha é toda preta, não há necessidade de pressionar a terceira linha.
(4) Configurações dos botões na quarta linha. A terceira luz da terceira linha é branca e precisa ser preta. Em seguida, a quarta linha deve pressionar o terceiro botão para fazer com que a luz branca acima se transforme em luz negra. Como resultado da Figura (4), a terceira linha é completamente preta. As coordenadas do botão que precisa ser pressionado na quarta linha são (3,2).
Após o término dessas quatro linhas, a quarta linha também ficou completamente preta, indicando que esta foi uma operação bem-sucedida e um total de 4 botões foram pressionados.
Existem 16 métodos de configuração de botões na primeira linha. Siga as etapas acima. Entre eles, aquele que pressiona menos botões é a resposta que resulta em todas as luzes pretas.
O texto acima assume que eles são todos pretos no final. Você também pode assumir que eles são todos brancos no final e fazer isso de novo. Pegar o menor número de botões é a resposta a esta pergunta.
Os leitores são solicitados a codificar de acordo com esta ideia. O código abaixo é para referência.
【Pontos-chave】 Pensamento.
Código C++
#include<bits/stdc++.h>
using namespace std;
char Map[11][11];
int n;
int GetBit(int x, int i){
//取出x的第i位
return (x >> i) & 1;
}
void SetBit(int &x, int i, int v){
//将x的第i位设置成v
if(v) x |= (1 << i);
else x &= ~(1 << i);
}
void FlipBit(int &x, int i){
//将x的第i位取反
x ^= (1 << i);
}
int solve(){
int oriLights[11]; //灯的初态
int Lights[11]; //按动按钮之后的灯的新状态
int result[11]; //存需要按动的按钮
int ans = n * n + 1; //需要按动的按钮数不会大于n*n
memset(oriLights, 0, sizeof(oriLights));
for(int i = 0; i < n; i++) //把灯用二进制的位来表示,第i行,第j列
for(int j = 0; j < n; j++){
if(Map[i][j] == 'b') SetBit(oriLights[i], j, 0); //0表示暗
else SetBit(oriLights[i], j, 1); //1表示亮
}
for(int k = 0; k < (1<<n); k++) {
//k是第0行的按钮,有0000~1111种按钮设置
memcpy(Lights, oriLights, sizeof(oriLights));
int switchs = k; //第0行的按钮,例如k=0011,就是按第1、2个按钮
for(int i = 0; i < n; i++) {
//逐一处理每行的灯
result[i] = switchs; //用result[i]记录第i行的按钮
for(int j = 0; j < n; j++) {
//逐一处理每一列的灯
if(GetBit(switchs, j)) {
if(j > 0) FlipBit(Lights[i], j-1); //j前面的第j-1灯变色
FlipBit(Lights[i], j); //第j个灯变色
if(j < n-1) FlipBit(Lights[i], j+1); //j后面的第j+1灯变色
}
}
if(i < n-1) Lights[i+1] ^= switchs; //修改下一行的灯
switchs = Lights[i]; //第i+1行按钮位置和第i行灯的位置相同
}
if(Lights[n-1] == 0) {
//最后一行也全变黑了,成功
int tmp = 0; //tmp为开关矩阵中1的数目
for(int i = 0; i < n; i++) //(i,j)就是需要按动的按钮坐标
for(int j = 0; j < n; j++)
if(result[i] & (1<<j))
tmp++;
ans = min(ans, tmp);
}
}
return ans;
}
int main(){
while(scanf("%d", &n) != EOF) {
memset(Map, 0, sizeof(Map));
for(int i = 0; i < n; i++) scanf("%s", Map[i]);
int ans = solve(); //以全黑为目标做一次
for(int i = 0; i < n; i++) //反过来以全白为目标做一次
for(int j = 0; j < n; j++)
if(Map[i][j] == 'b') Map[i][j] = 'w';
else Map[i][j] = 'b';
ans = min(ans, solve());
if(ans > n * n) puts("Impossible");
else printf("%d\n", ans);
}
return 0;
}
Código Java
import java.util.*;
public class Main {
static int GetBit(int x, int i) {
return (x >> i) & 1;
}
static int SetBit(int x, int i, int v) {
if (v == 1) x |= (1 << i);
else x &= ~(1 << i);
return x;
}
static int FlipBit(int x, int i) {
x ^= (1 << i);
return x;
}
static int solve(int n, String[] Map) {
int[] oriLights = new int[n];
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
if (Map[i].charAt(j) == 'b') oriLights[i] &= ~(1 << j);
else oriLights[i] |= (1 << j);
}
int ans = n * n + 1;
for (int k = 0; k < (1 << n); k++) {
int switchs = k;
int[] Lights = oriLights.clone();
int[] result = new int[n];
for (int i = 0; i < n; i++) {
result[i] = switchs;
for (int j = 0; j < n; j++) {
if (GetBit(switchs, j) == 1) {
if (j > 0) Lights[i] = FlipBit(Lights[i], j-1);
Lights[i] = FlipBit(Lights[i], j);
if (j < n-1) Lights[i] = FlipBit(Lights[i], j+1);
}
}
if (i < n-1) Lights[i+1] ^= switchs;
switchs = Lights[i];
}
if (Lights[n-1] == 0) {
int tmp = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if ((result[i] & (1 << j)) != 0)
tmp++;
ans = Math.min(ans, tmp);
}
}
if (ans > n * n) return -1;
else return ans;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
int n = scanner.nextInt();
if (n == 0) break;
String[] Map = new String[n];
for (int i = 0; i < n; i++) Map[i] = scanner.next();
int ans = solve(n, Map);
// 循环遍历数组并替换字符
for (int i = 0; i < n; i++) {
Map[i] = Map[i].replace('b', 'x'); // 将'b'替换为临时字符'x'
Map[i] = Map[i].replace('w', 'b'); // 将'w'替换为'b'
Map[i] = Map[i].replace('x', 'w'); // 将临时字符'x'替换为'w'
}
ans = Math.min(ans, solve(n, Map));
if (ans == -1) System.out.println("Impossible");
else System.out.println(ans);
}
scanner.close();
}
}
Código Python
import sys
def GetBit(x, i): return (x >> i) & 1
def SetBit(x, i, v):
if v: x |= (1 << i)
else: x &= ~(1 << i)
return x
def FlipBit(x, i):
x ^= (1 << i)
return x
def solve(n, Map):
oriLights = [0] * n
for i in range(n):
for j in range(n):
if Map[i][j] == 'b': oriLights[i]=SetBit(oriLights[i], j, 0)
else: oriLights[i]=SetBit(oriLights[i], j, 1)
ans = n * n + 1
for k in range(1 << n):
switchs = k
Lights = oriLights[:]
result = [0] * n
for i in range(n):
result[i] = switchs
for j in range(n):
if GetBit(switchs, j):
if j > 0: Lights[i] = FlipBit(Lights[i], j-1)
Lights[i] = FlipBit(Lights[i], j)
if j < n-1: Lights[i] = FlipBit(Lights[i], j+1)
if i < n-1: Lights[i+1] ^= switchs
switchs = Lights[i]
if Lights[-1] == 0:
tmp = 0
for i in range(n):
for j in range(n):
if result[i] & (1 << j):
tmp += 1
ans = min(ans, tmp)
return ans
for line in sys.stdin:
n = int(line.strip())
if n == 0: break
Map = []
for i in range(n): Map.append(input().strip())
ans = solve(n, Map)
F = {
}
F['b'] = 'w'
F['w'] = 'b'
for i in range(n):
Map[i] = ''.join([F[x] for x in Map[i]])
ans = min(ans,solve(n, Map))
if ans > n * n: print("Impossible")
else: print(ans)