派队灯 Party Lamps

题目:

题目描述

在IOI98的节日宴会上,我们有N(10<=N<=100)盏彩色灯,他们分别从1到N被标上号码。 这些灯都连接到四个按钮:

按钮1:当按下此按钮,将改变所有的灯:本来亮着的灯就熄灭,本来是关着的灯被点亮。

按钮2:当按下此按钮,将改变所有奇数号的灯。

按钮3:当按下此按钮,将改变所有偶数号的灯。

按钮4:当按下此按钮,将改变所有序号是3*K+1(K>=0)的灯。例如:1,4,7...

一个计数器C记录按钮被按下的次数。当宴会开始,所有的灯都亮着,此时计数器C为0。

你将得到计数器C(0<=C<=10000)上的数值和经过若干操作后某些灯的状态。写一个程序去找出所有灯最后可能的与所给出信息相符的状态,并且没有重复。

输入输出格式

输入格式:

不会有灯会在输入中出现两次。

第一行: N。

第二行: C最后显示的数值。

第三行: 最后亮着的灯,用一个空格分开,以-1为结束。

第四行: 最后关着的灯,用一个空格分开,以-1为结束。

输出格式:

每一行是所有灯可能的最后状态(没有重复)。每一行有N个字符,第1个字符表示1号灯,最后一个字符表示N号灯。0表示关闭,1表示亮着。这些行必须从小到大排列(看作是二进制数)。

如果没有可能的状态,则输出一行'IMPOSSIBLE'。

输入输出样例

输入样例#1:

10
1
-1
7 -1
输出样例#1:

0000000000
0101010101
0110110110


思路:

灯的个数可能很多,但是不难发现,灯的开关规律是6位一循环的,排列方式也就是ABCDEFABCDEFAB……这样的。

而这前6位数也只有8种情况,即:

000000

001110

010101

011011

100100

101010

110001

111111

可以通过十进制数存储:

000000    ——    0

001110    ——    14

010101    ——    21

011011    ——    27

100100    ——    36

101010    ——    42

110001    ——    49

111111    ——    63

可已发现按3次按以上每一种情况都可出现,而0、1、2次则要单独判断哪些情况可以按出来。


找出有可以按出的情况后就要判断是否符合题目要求的开关情况,此时只需把一个数的二进制拆出来判断即可。

bool judge(int x) {
	for(int i=1; i<=6; i++) {
		bool y=x%2;
		x/=2;
		if((y==false&&on[6-i+1])||(y==true&&off[6-i+1])) {
			return false;
		}
	}
	return true;
}

打印时要控制长度:

void print(int x) {
	int two[7]= {0};
	for(int i=1; i<=6; i++) {
		int y=x%2;
		x/=2;
		two[6-i+1]=y;
	}

	for(int i=1; i<=n; i++) {
		int y=i%6;
		if(y==0) y=6;
		printf("%d",two[y]);
	}
	printf("\n");
	return ;
}

代码:

#include<iostream>
#include<cstdio>
using namespace std;

int a[10]= {0,0,14,21,27,36,42,49,63};
int n,C;
bool on[6],off[6];

bool judge(int x) {
	for(int i=1; i<=6; i++) {
		bool y=x%2;
		x/=2;
		if((y==false&&on[6-i+1])||(y==true&&off[6-i+1])) {
			return false;
		}
	}
	return true;
}

void print(int x) {
	int two[7]= {0};
	for(int i=1; i<=6; i++) {
		int y=x%2;
		x/=2;
		two[6-i+1]=y;
	}

	for(int i=1; i<=n; i++) {
		int y=i%6;
		if(y==0) y=6;
		printf("%d",two[y]);
	}
	printf("\n");
	return ;
}

int main() {

	scanf("%d%d",&n,&C);

	int x;
	while(scanf("%d",&x)&&x!=-1) {
		int y=x%6;
		if(y==0) y=6;
		on[y]=true;
	}
	while(scanf("%d",&x)&&x!=-1) {
		int y=x%6;
		if(y==0) y=6;
		if(on[y]==true) {
			printf("IMPOSSIBLE");
			return 0;
		}
		off[y]=true;
	}

	bool flag=false;
	if(C>=3) {
		for(int i=1; i<=8; i++) {
			if(judge(a[i])) {
				print(a[i]);
				flag=true;
			}
		}
	}

	if(C==0) {
		if(judge(63)) {
			print(63);
			flag=true;
		}
	}

	if(C==1) {
		if(judge(0)) {
			print(0);
			flag=true;
		}
		if(judge(21)) {
			print(21);
			flag=true;
		}
		if(judge(42)) {
			print(42);
			flag=true;
		}
		if(judge(27)) {
			print(27);
			flag=true;
		}
	}

	if(C==2) {
		if(judge(0)) {
			print(0);
			flag=true;
		}
		if(judge(14)) {
			print(14);
			flag=true;
		}
		if(judge(21)) {
			print(21);
			flag=true;
		}
		if(judge(36)) {
			print(36);
			flag=true;
		}
		if(judge(42)) {
			print(42);
			flag=true;
		}
		if(judge(49)) {
			print(49);
			flag=true;
		}
		if(judge(63)) {
			print(63);
			flag=true;
		}
	}

	if(flag==false) {
		printf("IMPOSSIBLE");
	}

	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值