搜索与回溯 学习笔记
板子题
原题网址P1219 八皇后
八皇后
题目描述
一个如下的 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。
上面的布局可以用序列 来描述,第个数字表示在第 行的相应位置有一个棋子,如下:
行号
列号
这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前个解。最后一行是解的总个数。
输入格式
一行一个正整数,表示棋盘是大小的。
输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。
输入输出样例
输入 # 1
6
输出 #1
2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4
说明/提示
【数据范围】
对于的数据,。
题目翻译来自NOCOW。
USACO Training Section 1.5
算法理解
暴搜嘛,其实可以做的题目,但是其中的题目会TLE,得分一般看造数据的良心,一般只有~分,如果想A,对于一些数据比较弱的题目,只要剪枝优化或者使用记忆化搜索就可以了,但是对于像01背包这样的题目,还是改进算法更靠谱。
但是,每道题目,当想不出方法时,不能让分就白白流去。
方法一:骗分
方法二:暴搜(敲重点)
算法实现
使用一个递归函数即可。
代码实现
这里给出模板(伪代码)。
void search(int k){ if(到达目的地){ 输出解;return; } for(int i=1;i<=算符总数;i++){ if(不满足条件) continue; 保存结果; 进行递归前的操作; search(k+1); 恢复:递归前一步的内容; {回溯一步} } }
注意:这是我自己习惯的一个模板,有自己的码风,与书上的略有不同,勿喷。
算法复杂度
设总共有种独立的选择(类似于八皇后的几行,01背包的物品数量),每一种选择的选择方案(类似八皇后的列数,多重背包的每件物品的个数)为 那么,复杂度就为如果想八皇后、01背包一样,设那么复杂度为,非常慢,但是对于小数据还是非常爽的。
板子题-解析
先看一下数据范围:
在简单的算一下:
时间还是比较充裕的。
解法1
首先,开一个的数组来贮存这个位置是否被占取,利用全局变量的初值,0为未占取,1为已占取。
代码如下:
#include<cstdio> #define maxn 39 using namespace std; int n,see[maxn][maxn],ans[maxn],sum,a[maxn]; void search(int k){ if(k==n+1){ sum++; if(sum<=3){ for(int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n"); } return; } register int i,j; for(int i=1;i<=n;i++){ if(k==1){ for(int x=1;x<=n;x++) for(int y=1;y<=n;y++) see[x][y]=0; } if(see[k][i]==1) continue; a[k]=i; register int x=k,y=i; int now[maxn][maxn]={{0}}; for(x=1;x<=n;x++) for(y=1;y<=n;y++) now[x][y]=see[x][y]; for(int j=1;j<=n;j++) see[j][i]=1; x=k,y=i; while(1<=x&&x<=n&&1<=y&&y<=n){ see[x][y]=1; x++;y++; } x=k,y=i; while(1<=x&&x<=n&&1<=y&&y<=n){ see[x][y]=1; x++;y--; } x=k,y=i; while(1<=x&&x<=n&&1<=y&&y<=n){ see[x][y]=1; x--;y++; } x=k,y=i; while(1<=x&&x<=n&&1<=y&&y<=n){ see[x][y]=1;x--;y--; } search(k+1); for(x=1;x<=n;x++) for(y=1;y<=n;y++) see[x][y]=now[x][y]; } } int main(){ scanf("%d",&n); search(1); printf("%d",sum); return 0; }
测评结果:
ACACACACACACACTLE
he~~~~
TLE了一个
啊~~~~~~~~~~~~
可以看见,第个测试点已经是秒的了,看来,还得优化一波~~~~
当然是寄存器和右上角的O2啦
好了,不扯了,加下来请看——————
解法2--AC
二话不说,先上图:
Y\X | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | | | / | ||||||
2 | | | / | ||||||
3 | \ | | | / | |||||
4 | \ | | | / | |||||
5 | - | - | ▲ | - | - | - | - | - |
6 | / | | | \ | |||||
7 | / | | | \ | |||||
8 | | | \ |
▲表示皇后的位置
| - \ /表示皇后控制的区域,也就是说,如果皇后放在▲上,这些位置就不能放皇后了。
也就是说,我们只要开3个数组,因为我们是一行一行搜索的,就没必要统计“-”的位置了。
在仔细看,你会发现:
1.“|”永远位于同一列;
2.“\”行与列的差永远一样,由于C++不能处理负下标的数组(会RE),所以在用数组表示的时候,要加上一个数(这里用);
3.“/”行与列的和永远一样。
用 数组表示“|”,用l表示;
用 数组表示“/”
用 数组表示“\”(别忘了加上)
代码君献上AC代码:
注:这题我用了小时,
#include<cstdio> #define maxn 39 using namespace std; int n,line[maxn],l[maxn],r[maxn],sum,a[maxn]; void search(int k){ if(k==n+1){ sum++; if(sum<=3){ for(int i=1;i<=n;i++) printf("%d ",a[i]); printf("\n"); } return; } int i,j; for(int i=1;i<=n;i++){ if(line[i]==1||l[k+i]==1||r[k-i+n]==1) continue; a[k]=i; line[i]=1; l[k+i]=1; r[k-i+n]=1; search(k+1); line[i]=0; l[k+i]=0; r[k-i+n]=0; } } int main(){ scanf("%d",&n); search(1); printf("%d",sum); return 0; }
结尾福利:
祝同学们新年快乐!
祝武汉人民度过难关!
最后记得打月日(农历初四)~的月赛哦~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具