热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

状态压缩算法解决关灯问题2

本文介绍了一道经典的状态压缩题目——关灯问题2,并提供了解决该问题的算法思路。通过使用二进制表示灯的状态,并枚举所有可能的状态,可以求解出最少按按钮的次数,从而将所有灯关掉。本文还对状压和位运算进行了解释,并指出了该方法的适用性和局限性。

 

  • Step1 Problem

原题
现有n盏灯,以及m个按钮。每个按钮可以同时控制这n盏灯——按下了第i个按钮,对于所有的灯都有一个效果。按下i按钮对于第j盏灯,是下面3中效果之一:如果a[i][j]为1,那么当这盏灯开了的时候,把它关上,否则不管;如果为-1的话,如果这盏灯是关的,那么把它打开,否则也不管;如果是0,无论这灯是否开,都不管。
现在这些灯都是开的,给出所有开关对所有灯的控制效果,求问最少要按几下按钮才能全部关掉。

  • Step2 Ideas:

这题是一道比较经典的状态压缩题目(自己感觉就是用二进制代表某种状态,再以十进制表示二进制的数,枚举了全部的状态,DP起来复杂度岂不是很大?没错,状压其实是一种很暴力的算法,因为他需要遍历每个状态,所以将会出现2^n的情况数量,不过这并不代表这种方法不适用:一些题目可以依照题意,排除不合法的方案,使一行的总方案数大大减少从而减少枚举),这题需要对状压及位运算有一定的了解:首先要判断某一位的灯是开的还是关的,才能进行修改。对队首的某一状态,枚举每一个开关灯操作,记录到达这一新状态的步数(也就是老状态 + 1),若是最终答案,输出,若不是,压入队列。也就是说:我们把初始状态,用每个操作都试一遍,就产生了许多新的状态,再用所有操作一一操作新状态,就又产生了新的新状态,我们逐一尝试,直到有目标状态为止,这可以通过BFS实现。
前置技能:BFS, 位运算;
简单介绍一下位运算:
1.’&’符号,x&y,会将两个十进制数在二进制下进行与运算,然后返回其十进制下的值。例如3(11)&2(10)=2(10)。
2.’|’符号,x|y,会将两个十进制数在二进制下进行或运算,然后返回其十进制下的值。例如3(11)|2(10)=3(11)。
3.’ ^ ’符号,x ^ y,会将两个十进制数在二进制下进行异或运算,然后返回其十进制下的值。例如3(11)^2(10)=1(01)。
4.’<<’符号,左移操作,x<<2,将x在二进制下的每一位向左移动两位,最右边用0填充,x<<2相当于让x乘以4。相应的,’>>’是右移操作,x>>1相当于给x/2,去掉x二进制下的最有一位。
这四种运算在状压dp中有着广泛的应用,常见的应用如下:
1.判断一个数字x二进制下第i位是不是等于1。
方法:if ( ( ( 1 <<( i - 1 ) ) & x ) > 0)
将1左移i-1位,相当于制造了一个只有第i位上是1,其他位上都是0的二进制数。然后与x做与运算,如果结果>0,说明x第i位上是1,反之则是0。
2.将一个数字x二进制下第i位更改成1。
方法:x = x | ( 1<<(i-1) )
证明方法与1类似,此处不再重复证明。
3.把一个数字二进制下最靠右的第一个1去掉。
方法:x=x&(x-1)

  • Step3 Code:
#include 
#define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
const int maxn = 2e3+5;
int n, m;
struct node
{
    int dp, st;
};
int ma[maxn][maxn];
bool vis[maxn];

void bfs(int x)
{
    queue<node> q;
    node now;
    now.dp = x;
    now.st = 0;
    q.push(now);
    while(!q.empty())
    {
        node u = q.front();
        q.pop();
        for(int i = 1; i <= m; i++)
        {
            int a = u.dp;
            for(int j = 1; j <= n; j++)
            {
                if(ma[i][j] == 1)
                {
                    if((1 << (j - 1)) & a)
                    {
                        a = a ^ (1 << (j - 1));
                    }
                }
                else if(ma[i][j] == -1) a = (1 << (j - 1) | a);
            }
            if(vis[a]) continue;
            node v;
            v.dp = a;
            v.st = u.st + 1;
            if(!a)
            {
                vis[0] = true;
                cout << v.st << endl;
                return ;
            }
            q.push(v);
            vis[a] = true;
        }
    }
}

int main()
{
    cin >> n >> m;
    int num = (1 << (n)) - 1;
    for(int i = 1; i <= m; i++)
    {
        for(int j = 1; j <= n; j++)
        {
            cin >> ma[i][j];
        }
    }
    bfs(num);
    if(!vis[0]) cout << -1 << endl;
    return 0;
}


推荐阅读
  • 火星商店问题:线段树分治与持久化Trie树的应用
    本题涉及编号为1至n的火星商店,每个商店有一个永久商品价值v。操作包括每天在指定商店增加一个新商品,以及查询某段时间内某些商店中所有商品(含永久商品)与给定密码值的最大异或结果。通过线段树分治和持久化Trie树来高效解决此问题。 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • 本文详细介绍了 Dockerfile 的编写方法及其在网络配置中的应用,涵盖基础指令、镜像构建与发布流程,并深入探讨了 Docker 的默认网络、容器互联及自定义网络的实现。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • Explore how Matterverse is redefining the metaverse experience, creating immersive and meaningful virtual environments that foster genuine connections and economic opportunities. ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 数据库内核开发入门 | 搭建研发环境的初步指南
    本课程将带你从零开始,逐步掌握数据库内核开发的基础知识和实践技能,重点介绍如何搭建OceanBase的开发环境。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 本文探讨了如何在模运算下高效计算组合数C(n, m),并详细介绍了乘法逆元的应用。通过扩展欧几里得算法求解乘法逆元,从而实现除法取余的计算。 ... [详细]
  • Splay Tree 区间操作优化
    本文详细介绍了使用Splay Tree进行区间操作的实现方法,包括插入、删除、修改、翻转和求和等操作。通过这些操作,可以高效地处理动态序列问题,并且代码实现具有一定的挑战性,有助于编程能力的提升。 ... [详细]
  • 深度学习理论解析与理解
    梯度方向指示函数值增加的方向,由各轴方向的偏导数综合而成,其模长表示函数值变化的速率。本文详细探讨了导数、偏导数、梯度等概念,并结合Softmax函数、卷积神经网络(CNN)中的卷积计算、权值共享及池化操作进行了深入分析。 ... [详细]
  • 本文将介绍如何使用 Go 语言编写和运行一个简单的“Hello, World!”程序。内容涵盖开发环境配置、代码结构解析及执行步骤。 ... [详细]
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 帝国CMS多图上传插件详解及使用指南
    本文介绍了一款用于帝国CMS的多图上传插件,该插件通过Flash技术实现批量图片上传功能,显著提升了多图上传效率。文章详细说明了插件的安装、配置和使用方法。 ... [详细]
author-avatar
mobiledu2502930075
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有