百练 2811:熄灯问题【枚举】(题目分析 + 详细注释 + C++代码)

题目传送门

题意:

每个方格有一盏灯,有亮和灭两种状态

然后每个方格处也有一个按钮,按下这个按钮,会导致 当前方格和其上下左右的方格的灯的状态转变,即 亮 -> 灭,灭 -> 亮

求按按钮的方式

分析:

题目中也说了,按按钮的顺序不影响最后的结果(这是显然的,不说也应该知道)

可能的做法有:

1. 每个按钮有按和不按两种情况,如果直接枚举的话,那就是O(2 ^ 30),显然吃不消

2. 既然没办法枚举所有的按钮,那么就要想办法排除掉一些无用的情况

       我们可以先确定下来第一行的按钮状态,共 2 ^ 6 = 64种情况

        然后确定第二行的按钮状态。此时不是枚举了,因为只有第二行的按钮才能影响到第一行的灯,因此,如果在第一行的按钮按完之后,还有灯是亮着的,那么这个亮着的灯的下面一个按钮一定要按,否则这个亮着的灯就没办法熄灭了

        依次类推后面几行

可知,第二种方案明显减少了很多的枚举数量

其实代码就是枚举模拟了,需要注意很多细节:

#include<bits/stdc++.h>
using namespace std;

//矩阵的大小
int n = 5, m = 6;
//a是初始灯的状态,b是按钮的按压状态
int a[5][6], b[5][6], temp[5][6];
//四个方向
int d[][2] = {{-1, 0}, {0, 1}, {1, 0}, {0, -1}};

//检查下标是否越界
bool check(int x, int y){
	return x >= 0 && x < n && y >= 0 && y < m;
}

//灯的状态翻转
void tot(int x, int y){
	temp[x][y] = !temp[x][y];
}

//按压按钮
void press(int x, int y){
    //自身的状态翻转
	tot(x, y);
    //记录按压
	b[x][y] = 1;
	
	//影响四个方向
	for(int k = 0; k < 4; k++){
		int nx = x + d[k][0], ny = y + d[k][1];
		if(check(nx, ny)) tot(nx, ny);
	}
}

//判断当前是否还有亮灯
bool isOk(){
	for(int i = 0; i < n; i++) for(int j = 0; j < m; j++)
		if(temp[i][j]) return false;
	return true;
}


int main(){	
    //读入初始灯的状态    
	for(int i = 0; i < n; i++) for(int j = 0; j < m; j++) scanf("%d", &a[i][j]);
	
    //枚举第一行的按钮状态
	for(int i = 0; i < (1 << m); i++){
	    //每次都操作temp	
        memcpy(temp, a, sizeof a);
		//每次初始时,没有按钮被按,故初始为0
        memset(b, 0, sizeof b);
        //先把第一行需要按的按钮按了
		for(int j = 0; j < m; j++)
			if(i >> j & 1) press(0, j);
	    
        //枚举后面的每一行,根据上面一行的状态,决定自身是否需要按	
		for(int k = 1; k < n; k++)
			for(int j = 0; j < m; j++)
				if(temp[k - 1][j])
					press(k, j);

		//所有按钮都枚举完了,判断当前按法是否符和要求
		if(isOk()) 	break;
	}
	
    //输出
	for(int i = 0; i < n; i++){
		for(int j = 0; j < m; j++)
			printf("%d ", b[i][j]);
		puts("");
	}

	return 0;
}

如果觉得不错,不妨点个赞呗!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值