01串状态压缩(位运算)

题目描述:
农夫约翰对牛棚里昏暗的灯光感到不满,刚刚安装了一个新吊灯。
新吊灯由 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; 
}
    原文作者:_Anthea
    原文地址: https://blog.csdn.net/m0_62113052/article/details/122497290
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞