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-d 和 d-a 权 0,b-c 和 c-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;
}
相关推荐
cu1432 小时前
细谈IT65631的具体功能与其应用
c++·单片机·嵌入式硬件
幻智星科技2 小时前
Karatsuba与CSD优化FPGA大数乘法
c++·fpga开发·密码学
flower9803232 小时前
C++消息总线软件开发框架AI重构版
c++·设计模式·架构
lengxuenong2 小时前
树状数组 · 信息学学习手册3-优化LIS
c++·学习·算法
fanfan_hongyun3 小时前
【UE5.1 创建C++文件】
c++·ue5
余额瞒着我当琳3 小时前
C++11,发展历史浅谈,初始化列表和对象初始化,右值引用和移动语义
java·开发语言·c++·算法
Tsuki_tl3 小时前
【优选算法:双指针】——复写零题解
java·c++·算法·leetcode·数组·双指针·复写零
程序猿编码4 小时前
不用 PyTorch!从零手写 C++ 深度学习框架,自动微分、CUDA、分布式一把搞定
c++·深度学习·大模型推理·自动微分
real_metrix4 小时前
【学习笔记】C/C++ 头文件自包含
c++·c·头文件·自包含
雪落漂泊4 小时前
C++算法 滑动窗口篇(上)
开发语言·c++·算法