AcWing 95. 费解的开关

知识点:递归,递推

这个题有点难度,首先我们来分析题目,每个开关最多只用摁一下,因为摁两次相当于没有摁,以此类推,然后就是假如我们能够将它还原,那么我们按了一些开关,这些摁的的开关的先后次序是不影响的,影响的只有我们摁什么开关能让所有的灯都开开,而不是按这些开关的次序,最后就是,假如我们枚举第一行,后面的行为了能让所有的开关都开开,那么这时的方案是唯一的,这里就是递推,通过第一行的状态我们可以知道后面的行需要怎么按,然后在最后再判断一下最后一行,这样这个题的思路就出来了,

我们枚举第一行的按开关的情况,这个就是枚举第一行的列坐标,是指数型枚举,然后等我们到了递归的边界的时候,开始统计当前的按开关的方案,先按一下第一行,然后按后面的4行,每一行每个位置都是为了让前面的开关是不是开的而决定按不按,最后我们看一下最后一行的开关是不是都开了就行了,这个时间复杂度算一下就可以知道是够的

然后我30分钟看了答案才过的,是因为我递归的边界对所有开关的处理都是在输入数组里面处理的,这样当然是错的,,要用复制过来的数据,,,

#include 

using namespace std;

int ans;
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
vector v;
string t[5];

void solve(int x) {
	if (x == 5) {
		int temp = 0;
		string s[5];
		for (int i = 0; i < 5; i++) s[i] = t[i];
		for (int i = 0; i < v.size(); i++) {
			s[0][v[i]] = (s[0][v[i]] == '0' ? '1' : '0');
			for (int j = 0; j < 4; j++) {
				int x1 = 0 + dx[j];
				int y1 = v[i] + dy[j];
				if (x1 < 0 || x1 >= 5 || y1 < 0 || y1 >= 5) continue;
				s[x1][y1] = (s[x1][y1] == '0' ? '1' : '0');
			}
		}
		temp += v.size();
		for (int i = 1; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				if (s[i - 1][j] == '0') {
					s[i][j] = (s[i][j] == '0' ? '1' : '0');
					temp++;
					for (int k = 0; k < 4; k++) {
						int x1 = i + dx[k];
						int y1 = j + dy[k];
						if (x1 < 0 || x1 >= 5 || y1 < 0 || y1 >= 5) continue;
						s[x1][y1] = (s[x1][y1] == '0' ? '1' : '0');
					}
				}
			}
		}
		int ok = 1;
		for (int i = 0; i < 5; i++) {
			if (s[4][i] == '0') { ok = 0; break; }
		}
		if (ok) ans = min(ans, temp);
		return;
	}
	solve(x + 1);
	v.push_back(x);
	solve(x + 1);
	v.pop_back();
}

int main() {
	int T;
	cin >> T;
	while (T--) {
		ans = 100000000;
		for (int i = 0; i < 5; i++) cin >> t[i];
		solve(0);
		cout << (ans <= 6 ? ans : -1) << endl;
	}
	return 0;
}

你可能感兴趣的:(加入题解目录题解,c++,算法,蓝桥杯)