P4667 [BalticOI 2011] Switch the Lamp On复盘

电路板(旋转元件)题解复盘

模块:0-1 BFS / 图论建模

目标:将电路板转化为图模型,用 0-1 BFS 求最少旋转次数

基本信息

项目 内容
题目编号、来源 未知(电路板 / 旋转元件)
训练层级 B 图论建模
知识版块 0-1 BFS、建图、最短路

解题前・关键信号识别

维度 分析
目标、约束、底层结构 目标 :从左上角到右下角连通电源,最少旋转元件次数;约束 :N,M ≤ 500;底层结构:每个元件连接一对对角点,旋转即切换对角线。
数据规模 N×M ≤ 250000,点数为 (N+1)×(M+1) ≤ 251001,边数约为 4×N×M,0-1 BFS O(V+E) 可行。
候选算法和依据 0-1 BFS;依据:每次操作代价为 0(无需旋转)或 1(旋转一次),等价于求边权为 0/1 的最短路。
复杂度预判 时间复杂度 O((N+1)(M+1)),空间复杂度 O((N+1)(M+1))。

解题后・外化复盘

维度 内容
核心思路 把每个格点(电路交点)作为图节点,共有 (N+1)×(M+1) 个节点。每个元件占据一个方格,连接其两组对角顶点之一。根据当前字符决定哪些对角线已连接,未连接的对角线需要旋转一次。建图后求左上角 (0,0) 到右下角 (n,m) 的最短路。
建图方法 对于格子 (i,j),四个角:a = (i,j)b = (i,j+1)c = (i+1,j)d = (i+1,j+1)。若字符为 \,默认连接 a-d,需旋转才能连接 b-c,故加边 a-dd-a 权 0,b-cc-b 权 1。若字符为 /,默认连接 b-c,需旋转才能连接 a-d,故加边相反。
为什么用 0-1 BFS 边权只有 0 和 1,普通 BFS 不适用,Dijkstra 可行但 O(E log V) 较慢,0-1 BFS 用双端队列实现 O(V+E) 更优。
错因回溯 1. 节点编号计算错误,应为 id(x,y) = x*(m+1) + y;2. 建图时方向搞反,把 /\ 的边权弄颠倒;3. 忘记处理边界,导致越界;4. 用 BFS 而非 0-1 BFS,因为边权为 0 时普通队列会失效。
边界和易错点 1. 点数 = (n+1)*(m+1),索引从 0 开始;2. 字符 \ 在 C++ 中需要转义为 '\\';3. 0-1 BFS 中边权为 0 时 push_front,为 1 时 push_back;4. 无法到达时输出 NO SOLUTION(注意空格和大小写)。
下次看到什么信号,我应该想到这个方法 看到「旋转 90° 连接不同的对角线 + 求最少操作次数」,想到图建模 + 0-1 BFS。

AC 完整代码(按你提供的代码)

cpp 复制代码
#include <iostream>
#include <cstring>
#include <queue>
#include <algorithm>
#include <set>
#include <vector>
#include <deque>
using namespace std;
const int INF=0x3f3f3f;
int n,m;
int id(int x,int y){
	return x*(m+1)+y;
}
struct Edge{
	int to,w;
};
vector<vector<Edge>> g;
vector<int> dist;
int dx[4] = {-1,-1,1,1};
int dy[4] = {-1,1,-1,1};
void addedge(int u,int v,int w){
	g[u].push_back({v,w});
}
void bfs(int s){
	deque<int>dq;
	fill(dist.begin(),dist.end(),INF);
	dist[s]=0;
	dq.push_front(s);
	while(!dq.empty()){
		int u=dq.front();
		dq.pop_front();
		for(auto e:g[u]){
			int v=e.to;
			int w=e.w;
			if(dist[v]>dist[u]+w){
				dist[v]=dist[u]+w;
				if(w==0){
					dq.push_front(v);
				}else{
					dq.push_back(v);
				}
			}
		}
	}
}
int main(){
	cin>>n>>m;
	int cnt=(n+1)*(m+1);
	g.resize(cnt);
	dist.resize(cnt);
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++){
			char x;
			cin>>x;
			int a=id(i,j);
			int b=id(i,j+1);
			int c=id(i+1,j);
			int d=id(i+1,j+1);
			if(x=='\\'){
			   addedge(a,d,0);
			   addedge(d,a,0);
				addedge(c,b,1);
				addedge(b,c,1);
			   }else{
				   addedge(a,d,1);
				   addedge(d,a,1);
				   addedge(c,b,0);
				   addedge(b,c,0);
			}
		}
	}
	bfs(id(0,0));
	int ans=dist[id(n,m)];
	if(ans==INF) cout<<"NO SOLUTION";
	else cout<<ans;
	return 0;
}
相关推荐
江屿风2 小时前
【C++笔记】【二叉搜索树】流食般投喂
开发语言·数据结构·c++·笔记
海绵天哥3 小时前
LeetCode Hot 100 | 链表(下)· 分组翻转与设计(C++ 题解)
c++·leetcode·链表
choumin3 小时前
行为型模式——中介者模式
c++·设计模式·中介者模式·行为型模式
Lee_jerome13 小时前
《C/C++编译全家桶:g++四阶段、.a与.so区别、CMake构建、ARM交叉编译,这篇全讲透了》
c语言·开发语言·arm开发·c++·编译·cmake·cmakelists
冰心孤城14 小时前
C++ 与 C#混合编程 示例 (基于VS)
java·c++·c#
一只旭宝17 小时前
C++手写shared_ptr共享智能指针|原子引用计数、强弱引用控制块、赋值重载底层深度剖析
开发语言·c++·面试
开发者联盟league18 小时前
Java 通过 JNA 调用 C++ DLL:关键流程总结
java·c++·jna
库克克19 小时前
【C++】智能指针
java·开发语言·c++