费解的数字 递推+位运算
题目:
你玩过“拉灯”游戏吗?25盏灯排成一个5x5的方形。每一个灯都有一个开关,游戏者可以改变它的状态。每一步,游戏者可以改变某一个灯的状态。游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也要相应地改变其状态。我们用数字“1”表示一盏开着的灯,用数字“0”表示关着的灯。下面这种状态10111
01101
10111
10000
11011
在改变了最左上角的灯的状态后将变成:01111
11101
10111
10000
11011
再改变它正中间的灯后状态将变成:01111
11001
11001
10100
11011
给定一些游戏的初始状态,编写程序判断游戏者是否可能在6步以内使所有的灯都变亮。输入格式
第一行输入正整数n,代表数据中共有n个待解决的游戏初始状态。以下若干行数据分为n组,每组数据有5行,每行5个字符。每组数据描述了一个游戏的初始状态。各组数据间用一个空行分隔。输出格式
一共输出n行数据,每行有一个小于等于6的整数,它表示对于输入数据中对应的游戏状态最少需要几步才能使所有灯变亮。对于某一个游戏初始状态,若6步以内无法使所有灯变亮,则输出“-1”。数据范围
0<n≤500
输入样例:
3
00111
01011
10001
11010
1110011101
11101
11110
11111
1111101111
11111
11111
11111
11111
输出样例:3
2
-1
思路:
每一个位置顶多只会操作一次。因为如果操作两次的话,相当于不操作,必然是不满足最优解。
在一套方案中,操作的顺序无关紧要,
如果我们确定了第一行的操作方案的话,那么后面的行数都可以依此递推
代码如下:
#include <bits/stdc++.h>
using namespace std;
int a[7][7],b[7][7],n,answer;
void init(){//输入
getchar();
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
char ch=getchar();
a[i][j]=ch-'0';
}
getchar();//吸收换行符
}
}
void handle(int x,int y){//处理自己和四周的灯
b[x][y]^=1;
b[x-1][y]^=1;
b[x+1][y]^=1;
b[x][y-1]^=1;
b[x][y+1]^=1;
}
bool judge(){//判断是否达到全是0
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
if(!b[i][j]){
return false;
}
}
}
return true;
}
int work(){
answer=1e7;
//对第一行的枚举涵盖了该问题的整个状态空间
for(int i=1;i<=(1<<5);i++){//枚举第一行的点击方法,有2^5=32种
int ans=0;//每一轮都先令ans为0,后面更新answer
memcpy(b,a,sizeof(a));//将数组a复制到数组b
for(int j=1;j<=5;j++){
if(i>>(j-1)&1){
handle(1,j);//处理第一行
ans++;
}
}
for(int j=1;j<=4;j++){//因为是对j+1行操作,所以J是1-4
for(int k=1;k<=5;k++){//若二进制数的第k位是1,就操作第j+1行
if(!b[j][k]){
handle(j+1,k);
ans++;
}
}
}
if(judge()) answer=min(ans,answer);
}
return answer;
}
int main(){
cin>>n;
while(n--){
init();
if(work()<=6){
cout<<answer<<endl;
}
else cout<<"-1"<<endl;
}
return 0;
}
补充知识:
1.memcpy函数:
C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 复制 n 个字节到存储区 str1。
声明
下面是 memcpy() 函数的声明。
void *memcpy(void *str1, const void *str2, size_t n)
参数
- str1 -- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
- str2 -- 指向要复制的数据源,类型强制转换为 void* 指针。
- n -- 要被复制的字节数。
返回值
该函数返回一个指向目标存储区 str1 的指针。
与 strcpy() 不同的是,memcpy() 会完整的复制 num 个字节,不会因为遇到“\0”而结束。
【返回值】返回指向 dest 的指针。注意返回的指针类型是 void,使用时一般要进行强制类型转换。
2.位运算:
基本操作
与 & 都为真为真
或 | 有一个为真就为真
非 ! 真的变假,假的变真
异或 ^ 如果相同为0,不同为1
补码
int 32位 首位符号位,如果是0为正数,为1为负数。
1: 0000000000...01
2: 0000000000...10
3: 0000000000...11
补码:
[HTML_REMOVED]
1 + x = 000000000...00
x = 1111111111...11
1 + 1111111111...11 = 0000000000...00
2 + 1111111111...10 = 0000000000...00
x + ??????????...?? = 0000000000...00
? = ~x + 1
~x + 1 是 x 的补码
-n = ~n + 1
小技巧
- 数组初始化
memset(f,0x3f,sizeof(f))
- 左移运算符
<<
二进制 : 1 -> 10 -> 100 -> 1000
十进制 : 1 -> 2 -> 4 -> 8
综上所述:1 << n == 2^n
- 右移运算符
>>
二进制 : 1000 -> 100 -> 10 -> 1
十进制 : 8 -> 4 -> 2 -> 1
综上所述: n >> x == n / (2^x)
例题1a^b快速幂
求 a 的 b 次方对 p 取模的值。
输入格式
三个整数 a,b,p,在同一行用空格隔开。
输出格式
输出一个整数,表示a^b mod p
的值。
数据范围
$1≤a,b,p≤10^9$
输入样例:
3 2 7
输出样例:
2
题解:
按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn)。
$a^b$,那么其实b是可以拆成二进制的,该二进制数第i位的权为$2^{(i-1)}$,例如当b==11时,$a^{11}=a^{(2^0+2^1+2^3)}$ 。
11的二进制是1011,11 = 2³×1 + 2²×0 + 2¹×1 + 2º×1,因此,我们将a¹¹转化为算 $a^(2^0)a^(2^1)a^(2^3) $ 。
由于是二进制,很自然地想到用位运算这个强大的工具: & 和 >> ,&运算通常用于二进制取位操作,例如一个数 & 1 的结果就是取二进制的最末位。还可以判断奇偶x&1==0为偶,x&1==1为奇。>>运算比较单纯,二进制去掉最后一位 。
代码:
#include<bits/stdc++.h>
using namespace std;int a,b,mod;int main(void)
{cin >> a >> b >> mod;int res = 1 % mod;while(b){if(b&1) res = 1ll * res * a % mod;a = 1ll * a * a % mod;b >>= 1;}cout << res;return 0;
}
例题2:64位整数乘法
求 a 乘 b 对 p 取模的值。
输入格式
第一行输入整数a,第二行输入整数b,第三行输入整数p。
输出格式
输出一个整数,表示a*b mod p
的值。
数据范围
1≤a,b,p≤10^18
输入样例:
3
4
5
输出样例:
2
题解:
a * b
a + a + a + a + a + a +...+ a
a * 1 = a
a * 2 = 2a
a * 3 = 3a
...
a * (2^k) = 2^k * a
代码:
#include<bits/stdc++.h>
using namespace std;long long a,b,mod;int main(void)
{cin >> a >> b >> mod;long long ans;while(b){if(b&1) ans = (ans + a) % mod;a = a * 2 % mod;b >>= 1;}cout << ans << endl;return 0;
}
费解的数字 递推+位运算相关推荐
- LeetCode 260. 只出现一次的数字 III(位运算)
1. 题目 给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次. 找出只出现一次的那两个元素. 示例 :输入: [1,2,1,3,2,5] 输出: [3,5] 注意: 结 ...
- 程序员面试金典 - 面试题 17.19. 消失的两个数字(数学/位运算)
1. 题目 给定一个数组,包含从 1 到 N 所有的整数,但其中缺了两个数字. 你能在 O(N) 时间内只用 O(1) 的空间找到它们吗? 以任意顺序返回这两个数字均可. 示例 1: 输入: [1] ...
- JSK-115 单独的数字(二)【位运算】
单独的数字(二) 一个整型数组中有一个元素只出现一次,其它元素都出现两次.求出只出现一次的元素. 要求: 线性时间复杂度,不能使用额外空间. 聪明的你能搞定吗? 格式: 第一行输入数字n,代表有n个数 ...
- 程序员面试金典 - 面试题 17.04. 消失的数字(数学/位运算)
1. 题目 数组 nums 包含从0到n的所有整数,但其中缺了一个. 请编写代码找出那个缺失的整数.你有办法在O(n)时间内完成吗? 注意:本题相对书上原题稍作改动 示例 1: 输入:[3,0,1] ...
- 260. 只出现一次的数字 III 【位运算】
https://leetcode-cn.com/problems/single-number-iii/ 首先成对的都会删除.只会剩下两个不是一对的数. 首先不难看出 两个数不同,异或后一定会有一个1. ...
- leetcode 137. 只出现一次的数字 II(位运算)
给你一个整数数组 nums ,除某个元素仅出现 一次 外,其余每个元素都恰出现 三次 .请你找出并返回那个只出现了一次的元素. 示例 1: 输入:nums = [2,2,3,2] 输出:3 示例 2: ...
- leetcode 260. Single Number III | 260. 只出现一次的数字 III(位运算:分组异或)
题目 https://leetcode.com/problems/single-number-iii/ 题解:分组异或 参考1:讨论区题解 you know you can eliminate dou ...
- 你所不知道的按位运算
本文转载来之微信公众号编程派 https://mp.weixin.qq.com/s?__biz=MzAwNDc0MTUxMw==&mid=2649639595&idx=1&sn ...
- c语言用位运算编程2的n次方,C语言中使用位运算编写程序
1. 一组数据中只有一个数字出现了一次. 其他所有数字都是成对出现的.请找出这个数字.(使用位运算) 分析:要找出一组数据中单独的数,就将数组中的每个数全都取异或运算. 程序如下: #inclu ...
- 费解的开关(位运算+递推)
题目描述: 你玩过"拉灯"游戏吗?25盏灯排成一个5x5的方形.每一个灯都有一个开关,游戏者可以改变它的状态.每一步,游戏者可以改变某一个灯的状态.游戏者改变一个灯的状态会产生连锁 ...
最新文章
- vue 路由按需加载
- 数学狂想曲(十二)——熵(2), 阴影面积, 肺炎版《黄冈密卷》
- jquery 事件对象属性小结
- WM中的OutLook开发和操作
- Prism区域异常问题分析(导航失效?)
- C++之inline函数
- 很认真地聊一聊程序员的自我修养
- Qt 设置当前窗口出现在左右窗口的最前面
- python闭包怎么理解_Python 闭包的理解
- 国内外独立IP行情及网站用独立IP优势面面观
- 三星雪上加霜?高通骁龙875或将回归台积电
- Android平板app图标,安卓手机应用图标显示为默认的机器人,平板显示正常
- hdoj 4526 威威猫系列故事——拼车记
- php 获取array keys,php数组函数序列之
- 媒体查询@media scree
- 如何使用ArcMap将Excel数据转换为shp数据
- Android 12 WiFi 框架
- 机器学习经典案例——泰坦尼克号
- 帝国cms php超时,帝国CMS后台登录超时、登录错误5次限制的解决办法
- PHP 把ofd格式文件转PDF,打开OFD格式文件及将OFD格式文件转换成PDF文件
热门文章
- 更新来袭!新增语音添加待办、邮箱通知等功能
- hgame 2022 逆向 reverse 部分题目 Writeup
- 用c语言编程点菜系统,基于C语言实现点菜系统.pdf
- 【Unity学习】Unity GetCurrentAnimatorStateInfo方法判断动画播放
- The day that you see me old
- 计算机改变世界英语作文,2013年3月3日托福独立写作范文:年轻人改变世界(英文版)...
- 扩展欧几里得___追风少年的坐骑(2016swust信息院赛)
- 基于javaweb的人才求职招聘管理系统(java+springboot+freemarker+jpa+mysql)
- asp.net mvc 实现判断用户是否登录的两种方式
- 易捷行云新一代私有云全场景智能统一运维|轻运维之场景化运维