《算法竞赛·快冲300题》每日一题:“点灯游戏”

news2025/1/21 7:21:36

算法竞赛·快冲300题》将于2024年出版,是《算法竞赛》的辅助练习册。
所有题目放在自建的OJ New Online Judge。
用C/C++、Java、Python三种语言给出代码,以中低档题为主,适合入门、进阶。

文章目录

  • 题目描述
  • 题解
  • C++代码
  • Java代码
  • Python代码

点灯游戏” ,链接: http://oj.ecustacm.cn/problem.php?id=1134

题目描述

【题目描述】 有一个n*n的灯泡矩阵,b表示灯暗,w表示灯亮。每个灯的位置上都有控制着这盏灯的按钮。当按下一个按钮后,该按钮以及周围位置(上下左右)的灯都会改变状态(亮->暗,暗->亮)。最少按下多少个按钮可以使得所有的灯都亮或者都暗。
【输入格式】 输入有多组数据,每组数据第一行有一个整数n(1≤n≤10),接下来n行,每行n个字符表示初始的灯泡矩阵。
【输出格式】 如果可以使得所有的灯泡都亮或者都暗,输出最少按下的按钮数目。如果无法达到,输出"Impossible"(不含引号) 。
【输入样例】

4
bwwb
bbwb
bwwb
bwww
4
bwbw
wwww
bbwb
bwwb

【输出样例】

4
Impossible

题解

   “点灯游戏”是一个经典问题,有多种解法。
  如果没有限制“最少按钮数”,只要求能实现全暗或全灭,如何操作?这里介绍一种被称为“首行穷举法”的方法,简单易行。设目标是把所有的灯变黑(暗),操作如下:
  (1)第一行不按动按钮,只需找到白灯的位置;
  (2)第二行对应第一行的白灯的位置,都是按钮,按下后,它上下左右的灯都变色,特别是上一行的白灯都会变黑,从而保证第一行都变成黑色;
  (3)每一行的按钮,都对应上一行的白灯,使上一行全变黑。
  本题要求得“最少按钮数”,可以把所有按钮都试一遍,找到最少的那种。为了减少尝试的次数,可以结合上述方法。本题的n≤10,规模很小,这种暴力法也可行。
  假设目标是最后都变成黑色,从第一行开始,逐行按动按钮。
  第一行有0000~1111共16种按按钮的方法。0000表示1个都不按,0001表示只按第1个按钮,0010表示只按第2个按钮,0011表示按第1、第2个按钮,…,等等。
  第一行选择一种方法按完之后,继续按动第二行。第二行如何按?显然要保证第一行都变成黑色才行。那么应该让第二行的按钮位置跟第一行的白色位置一样,因为第二行的按钮按动之后,它上面的白色会跟着变成黑色。
  按上述规则继续按后面的行,直到结束。
  下面举例说明。图中的“初态”是第一个样例的灯,黑表示暗,白表示亮。左上角坐标(0,0),右下角坐标(3,3)。
在这里插入图片描述
  (1)第一行的按钮设置。以第一行按0011为例,就是按第1、2个按钮。按第1个按钮(0,0)后得到图(1),再按第2个按钮(0,1)后得到图(2)。记录两个按钮的坐标(0,0)、(0,1)。
  (2)第二行的按钮设置。此时第一行只有第2个灯是白的,需要变成黑色。那么第二行必须按第2个按钮,才能让上面的白灯变成黑灯。第二行需要按的按钮的坐标是(1,1),得到图(3)的结果,第一行全黑了。
  (3)第三行的按钮设置。由于第二行全是黑的,所以第三行不用按了。
  (4)第四行的按钮设置。第三行的第3个灯是白的,需要变成黑色。那么第四行必须按第3个按钮,才能让上面的白灯变成黑灯。得到图(4)的结果,第三行全黑了。第四行需要按的按钮坐标是(3,2)。
  这四行结束后,第四行也变成了全黑,说明这是一次成功的操作,共按了4个按钮。
  第一行共有16种按钮设置方法,都按以上步骤操作一遍,其中按动按钮最少的就是结果全是黑灯的答案。
  以上假设最后都是黑色,也可以假设最后都是白色,再操作一次即可。取最少的按钮次数就是本题的答案。
  请读者按这个思路编码。下面的代码供参考。
【重点】 思维 。

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;
}

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();
    }
}

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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1007349.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何通过局域网共享文件

需求环境&#xff1a;公司有多个部门&#xff0c;不同用户与部门使用不同文件 业务背景&#xff1a;一台windows服务器,若干客户端 需要技能&#xff1a; 熟悉windows系统管理&#xff0c; 包含计算机管理、 硬盘硬件、 用户管理、 share and storage management 项目方案…

系统架构设计专业技能 · 软件工程之UML建模设计

现在的一切都是为将来的梦想编织翅膀&#xff0c;让梦想在现实中展翅高飞。 Now everything is for the future of dream weaving wings, let the dream fly in reality. 点击进入系列文章目录 系统架构设计高级技能 软件工程之UML建模设计 一、需求分析 - UML图二、用例图2.…

黑马JVM总结(六)

&#xff08;1&#xff09;常量池 方法区的组成中都由一个叫做运行时常量池的部分&#xff0c;内部包含一个叫做StringTable的东西 反编译二进制字节码&#xff1a; 类的基本信息&#xff1a; 常量池&#xff1a; 方法定义&#xff1a; 构造方法 main方法 &#xff1a;方法中…

NS6326 4-30V 输入 5V/3A 输出同步降压稳压器芯片

NS6326 4-30V 输入 5V/3A 输出同步降压稳压器芯片 1 特性  宽输入电压范围&#xff1a;4V-30V  效率可高达 92%以上  超高恒流精度&#xff1a;5%  恒压精度&#xff1a;5%  无需外部补偿  开关频率&#xff1a;130kHz  输入欠压/过压、输出短路和过热保护  SO…

驱动开发--自动创建设备节点udev机制的实现过程

一、udev的认识 udev:自动创建设备节点的机制&#xff0c;创建设备节点的逻辑在用户空间 二、udev机制创建设备节点的过程分析 三、目录信息创建和删除类函数 #include<linux/device.h> 1.向上提交目录信息 struct class * class_create(struct module *owner,const c…

牛客:小美的01串翻转

小美的01串翻转 #include<iostream> #include<cstring> #include<string> #include<vector>using namespace std; typedef long long ll; const int N 1100; string s; ll res 0;int main() {cin>>s;int n s.size();vector<vector<in…

spice VDAgent简介

vdagent分为linux和windows&#xff0c;其中Linux分为vdagent守护进程和vdagent客户端进程&#xff0c;而windows主要为vdagent服务。 在windows中&#xff0c;通过服务方式自启动&#xff0c;并控制windows显示等。 在linux中&#xff0c; 守护进程通过 Sys-V initscript 或 s…

ChatGPT追祖寻宗:GPT-2论文要点解读

论文地址&#xff1a;Language Models are Unsupervised Multitask Learners 上篇&#xff1a;GPT-1论文要点解读 在上篇&#xff1a;GPT-1论文要点解读中我们介绍了GPT1论文中的相关要点内容&#xff0c;其实自GPT模型诞生以来&#xff0c;其核心模型架构基本没有太大的改变&a…

线程安全问题(3)--- wait(),notify()

前言 在多线程的环境下&#xff0c;我们常常要协调多个线程之间的执行顺序&#xff0c;而为了实现这一点&#xff0c;Java提供了一些方法来帮助我们完成这一点。 一&#xff0c;wait() 作用&#xff1a; 使当前线程进入等待状态 释放当前的锁 (即该方法必须和 synchrnized 关键…

【02】Charles_Breakpoints给接口设置断点

目录 1.适用场景&#xff1a; 2.操作步骤 3.实现效果 ​编辑 1.适用场景&#xff1a; 测试工作中&#xff0c;有些接口的请求or响应数据不符合预期&#xff0c;需要mock一些数据时&#xff0c;需要修改接口的request请求参数、response响应数据。 就可以借助断点实现。 …

探访天府蜂巢成都直播基地,全成都前十的直播产业供应链都在这!

随着新一轮科技革命和产业变革深入发展&#xff0c;数字化转型已经成为大势所趋。成都直播基地作为数字经济创新发展的前沿和焦点&#xff0c;为产业转型升级和数字经济发展提供核心驱动力。 “直播”新业态新模式的兴起&#xff0c;显示出强大的潜力和活力&#xff0c;树莓集团…

《动手学深度学习 Pytorch版》 4.8 数值稳定性和模型初始化

4.8.1 梯度消失和梯度爆炸 整节理论&#xff0c;详见书本。 梯度消失 %matplotlib inline import torch from d2l import torch as d2lx torch.arange(-8.0, 8.0, 0.1, requires_gradTrue) y torch.sigmoid(x) y.backward(torch.ones_like(x))d2l.plot(x.detach().numpy()…

F. Magic Will Save the World(DP)

Problem - F - Codeforces 黑暗势力的传送门在世界边界打开了&#xff0c;现在整个世界都面临着可怕的威胁。为了关闭传送门并拯救世界&#xff0c;你需要一个接一个地击败n个从传送门中出现的怪物。 只有女巫Vika能够应对这个威胁。她有两个魔法力量——水之魔法和火之魔法。…

口袋参谋:淘宝卖家必备的淘词分析神器!

​在淘宝天猫上&#xff0c;每天都有数以亿计的商品在上面交易&#xff0c;在这个巨大的市场中&#xff0c;如何让自己的商品脱颖而出&#xff0c;成为卖家们关心的问题之一。 因此关键词的挑选就显得尤为重要&#xff0c;好的关键词&#xff0c;一天的搜索量可达上万&#xf…

模拟实现字符串函数和内存函数

模拟实现字符串函数和内存函数 函数介绍部分模拟实现strlenstrcpy,strcat,strcmpstrncpy,strncat,strncmpstrstr,strtokstrerror 字符分类函数内存函数memcpy,memmove,memset,memcmp 求字符串长度(strlen)长度不受限制的字符串函数(strcpy,strcat,strcmp)长度受限制的字符串函数…

数据结构与算法之分治法

文章目录 前言1.递归解决阶乘函数2.归并排序算法2.1 归并排序的概念2.2 分治法的三步曲2.3 归并排序的动画2.4 归并排序算法(C语言代码) 3.最大子序列和问题3.1 问题的定义3.2 分治的思路3.3 简单的分解下代码的结果3.4 算法代码3.5 测试结果 前言 分治法首先需要明白递归的概…

Ubuntu18.04遇到的nodejs的坑记录

Ubuntu18.04安装nodejs的正确姿势 问题回顾 给我的博客网站整上代码高亮插件&#xff0c;在本地运行一切完美&#xff0c;可在我的Ubuntu18.04 bionic版本服务器上运行却报了以下的错误 ERROR in ./node_modules/highlight.js/lib/languages/xml.js Module parse failed: Er…

安装最新版React devtool

1.按照官方文档执行 我这里只想在项目里安装&#xff0c;因此执行的 npm install react-devtools^4 2.安装完成后&#xff0c;在 package.json里加入启动命令 "script": {"dev": "react-devtools" }执行npm run dev会弹出一个窗口 如果是全局…

Redis从简单到高级的总结(超详细)

Redis简单 1、Redis Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件。**官网&#xff1a;**https://redis.io **中文网&#xff1a;**https://www.redis.net.cn/ **key-value结构存储&#xff1a;****主要特点&#xff1a;**- 基…

Vue3 监听属性-watch

文章目录 Vue3 监听属性-watch1. 概念2. 实例2.1 通过使用 watch 实现计数器2.2 千米与米之间的换算2.3 异步加载中使用 watch2.4 小计 Vue3 监听属性-watch 1. 概念 Vue3 监听属性 watch&#xff0c;可以通过 watch 来响应数据的变化。 watch 的作用&#xff1a;用于监测响应…