字节跳动笔试第三题解题思路分享(3月14日,环形按钮问题)

字节跳动 笔试第三题
输入一个bool类型的数组,首尾相接组成一个圆环
每个位置都有一个按钮,按钮按下以后自身及相邻位置改变状态(0->1;1->0)
求把所有位置都置为1所需要的最少按键次数
输入一个n代表数组长度,输入n个数字代表按钮状态

例题:
输入
10
0 1 1 0 0 0 1 0 0 1
输出
4

说明:
按下第二个位置
1 0 0 0 0 0 1 0 0 1
按下第三个位置
1 1 1 1 0 0 1 0 0 1
按下第六个位置
1 1 1 1 1 1 0 0 0 1
按下第八个位置
1 1 1 1 1 1 1 1 1 1
一共按了4次按钮,所以输出4

补充:
0 < n <= 16

解法:
BFS搜索算法
遇到前一个按钮/当前按钮/后一个按钮状态是 0 的情况
把按下按钮后的状态记录入队,并判断按下按钮后的状态是否满足条件
如果满足条件,返回按下按钮的次数
如果不满足条件,继续遍历

更多案例:
16
0 0 1 0 0 0 1 1 0 1 0 0 1 0 0 1
int sub(int a, int b, int size) {
	return a - b < 0 ? a + size - b : a - b;
}

int add(int a, int b, int size) {
	return a + b >= size ? a + b - size : a + b;
}

bool judge(vector<int> status) {
	return count(status.begin(), status.end(), 0) == 0;
}

bool button(queue<vector<int>> &qv, vector<int> status, int index) {
	int size = status.size();
	status[index] = 1 - status[index];
	status[sub(index, 1, size)] = 1 - status[sub(index, 1, size)];
	status[add(index, 1, size)] = 1 - status[add(index, 1, size)];
	qv.push(status);
	return judge(status);
}


int BFS(queue<vector<int>> qv) {
	int cnt = 0;
	queue<vector<int>> temp = qv;
	if (judge(qv.front())) {
		return cnt;
	}
	while (!qv.empty()) {
		while (!qv.empty()) {
			int size = qv.front().size();
			for (int i = 0; i < qv.front().size(); i++) {
				if (qv.front()[sub(i, 1, size)] == 0 || qv.front()[i] == 0 || qv.front()[add(i, 1, size)] == 0) {
					if (button(temp, qv.front(), i))
						return cnt + 1;
				}
			}
			qv.pop();
		}
		qv = temp;
		cnt++;
		if (cnt > 6)
			return -1;
	}
}

int main() {
	int n;
	while (cin >> n) {
		vector<int> status(n);
		for (auto &b : status)
			cin >> b;
		queue<vector<int>> qv;
		qv.push(status);
		cout << BFS(qv) << endl;
	}
	return 0;
}
看完有人会觉得这个算法时间复杂度是不是太大了
事实上确实很大,如果最终遍历次数是N
那最坏的时间复杂度就是O(16^N)
并且当我用“更多案例”进行测试时
程序运行了将近半分钟,此时temp队列的遍历数超过百万
时间复杂度和空间复杂度都不满足条件
但这已经是我能想到的唯一办法了,也希望大佬可以补充和纠正,谢谢

#笔经##Java工程师##字节跳动#
全部评论
牛逼
1 回复 分享
发布于 2021-03-16 12:20
dfs ```c++ #include <iostream> using namespace std; const int N = 20; int q[N]; int n; bool judge() { for(int i = 0; i < n; i++) { if(q[i] != 1) return false; } return true; } bool st; int cnt, res = 1e6; void dfs(int u) { //u表示当前正在操作的u个灯泡,有两种情况,按下或者不按 //首先判断是否已经全为1了。 if(judge()) { st = true; res = min(res, cnt); return; } if(u == n) { //按完了灯泡,返回  return; } //不按第u个灯泡 dfs(u + 1); //按下第u个灯泡 q[u] = !q[u]; q[(u + 1) % n] = !q[(u + 1) % n]; q[(u - 1 + n) % n] = !q[(u - 1 + n) % n]; cnt++; dfs(u + 1); //恢复现场  q[u] = !q[u]; q[(u + 1) % n] = !q[(u + 1) % n]; q[(u - 1 + n) % n] = !q[(u - 1 + n) % n]; cnt--; } int main() { cin >> n; for(int i = 0; i < n; i++) cin >> q[i]; dfs(0); if(!st) res = -1;  cout << res; return 0; } ```
点赞 回复 分享
发布于 2021-03-15 17:18
借楼问一下第二题怎么写啊?最好能发一下代码
点赞 回复 分享
发布于 2021-03-15 17:19
回溯就完事了
点赞 回复 分享
发布于 2021-03-16 14:29
高斯消元可以做,复杂度n^3
点赞 回复 分享
发布于 2021-03-16 14:30
楼上思路😅
点赞 回复 分享
发布于 2021-03-16 20:05
虽然没写出来, 但我当时想的是16位直接转为int类型,一个二进制位对应一个灯泡,按下开关等于与连续的3位异或,从最终目标开始bfs,应该可以直接算出所有可能。
点赞 回复 分享
发布于 2021-03-16 21:27

相关推荐

2024-12-16 19:50
已编辑
香港中文大学 前台
点赞 评论 收藏
分享
评论
1
4
分享

创作者周榜

更多
牛客网
牛客企业服务