题目描述:
农夫约翰对牛棚里昏暗的灯光感到不满,刚刚安装了一个新吊灯。
新吊灯由 N 个灯泡组成,这 N 个灯泡围成一圈,编号为 0∼N−1。
奶牛对这个新吊灯非常着迷,并且喜欢玩以下游戏:
对于第 i 个灯泡,如果在 T−1 时刻,它左侧的灯泡(当 i>0时,为第 i−1 个灯泡;当 i=0时,为第 N−1 个灯泡-)是开着,那么在 T时刻,就切换这个灯泡的状态。
这个游戏将持续 B 单位时间。
给定灯泡的初始状态,请确定在 B单位时间后,它们的最终状态。
输入格式:
第一行包含两个整数 N和 B。
接下来 N 行,按顺序描述每个灯泡的初始状态,每行包含一个整数 1(表示开)或 0(表示关)。
输出格式:
共 N行,按顺序每行输出一个灯泡的最终状态。
数据范围:
3≤N≤16,
1≤B≤1015
输入样例:
5 6
1
0
0
0
0
输出样例:
1
1
1
0
1
样例解释:
灯泡状态如下:
时刻 T=0: 1 0 0 0 0
时刻 T=1: 1 1 0 0 0
时刻 T=2: 1 0 1 0 0
时刻 T=3: 1 1 1 1 0
时刻 T=4: 1 0 0 0 1
时刻 T=5: 0 1 0 0 1
时刻 T=6: 1 1 1 0 1
#include<stdio.h>
#include<string.h>
int N = 1 << 16; //最多有(1*2的16次方)种不同的状态
int n;
long long m;
int upstate(int state)
{
int res = 0; //state表示当前状态,res表示下一个状态
for(int i = 0; i < n; i ++ ) //枚举每一位
{
int j = (i - 1 + n) % n; //i-1表示前一位,+1是因为可能存在负数
int x = state >> i & 1; //state的第i位
int y = state >> j & 1; //state的第j位
res |= (x ^ y) << i;
}
return res;
}
void print(int state) //将十进制数转化成二进制数,输出每一位
{
for(int i = 0; i < n; i ++ )
{
printf("%d\n",state >> i & 1);
}
}
int main()
{
int state = 0;
int p[N]; //每个状态是第几步走到的
scanf("%d %lld", &n, &m);
for(int i = 0; i < n; i ++ )
{
int x;
scanf("%d",&x);
state |= x << i; //第i位是x,把x加到state的第i位上
}
memset(p, -1, sizeof(p));
p[state] = 0;
for(int i = 1; ; i ++) //枚举步数
{
state = upstate(state);
if (i == m) //在形成环之前已经走完m步了
{
print(state);
break;
}
else if (p[state] == -1) p[state] = i; //当前状态未被初始化
else //这个状态走过了,即发现一个环
{
int len = i - p[state]; //环的长度
int r = (m - i) % len; //还需要走r步
while(r -- ) state = upstate(state);
print(state);
break;
}
}
return 0;
}
01串状态压缩:对于只由0和1组成的数字序列(01串),除了用字符串存储,还可以把01串看成一个二进制数,转化成十进制存到一个int里,用int来表示一个状态。
因为灯泡只有开(1)和关(0)两种状态,且3 <= N <=16,所以最多有216种不同的状态,第216+1种状态一定会与前面某个状态相同,当时间足够长时,灯泡的状态变化会形成一个环。
位运算:
按位与 &
如果 x == 1 且 y == 1,则(x & y) = 1,否则(x & y) = 0
应用:让某一位或某几位为0;取一个数中的一段。按位或 |
如果 x == 1 或 y == 1,则(x | y) = 1,否则(x | y) = 0
应用:让某一位或某几位为1;把两个数拼起来 。按位取反 ~
~x = 1 -x ,把1位变0,0位变1。按位异或 ^
如果 x == y ,则(x ^ y)= 0,否则(x ^ y)= 1;
x ^ y ^ y —> x。左移 <<
i << j : i 中所有的位向左移动 j 个位置,而右边填入0;
x << = 1 等价于 x *= 2; x << = n 等价于 x *=2n。右移 >>
i >> j : i 中所有的位向右移动 j 个位置;
对于unsigned的类型,左边填入0;对于signed的类型,左边填入原来的最高位(保持符号不变);
x >>= 1 等价于 x /= 2; x >> = n 等价于 x /=2n。
输出一个二进制数:
#include<stdio.h>
int main()
{
int number;
scanf("%d",&number);
unsigned mask = 1u << 31; //共32个比特位,最高位为1
for( ; mask; mask >>= 1){
printf("%d",number & mask ? 1 : 0); //如果取&的结果不等于0,则输出1,否则输出0
}
return 0;
}