两个NOI题目的启迪8皇后和算24
论出于什么原因和目的,学习C++已经有一个星期左右,从开始就在做NOI的题目,到现在也没有正式的看《Primer C++》,不过还是受益良多,毕竟C++是一种”低级的高级语言“,而且NOI上的题目可以说是循序渐进。不仅仅是从ASM、VB.NET的角度看编程语言,这让我对编程语言语言的理解有了一些深入。
这一篇来记录一下”2.5基本算法之搜索“的两个问题解决的过程。它们有一些类似之处,也有很大不同。
一、8皇后
这是一个非常经典的问题:输出在8*8的棋盘上,摆放8个相互无法吃掉的皇后的全部摆法。一般认为有92种摆法,其中有一些通过镜像、旋转相互重合。因为皇后可以横向、纵向、斜向4个方向吃子且远近不计,所以放上一个皇后之后就会导致一些格子没法再放皇后了:典型的,我们在(0,0)放上一个皇后之后,下次再放就要避开满足x=0,y=0,x=y的格子,这也是简化算法的基础。这导致这个问题求解时,可以按行或按列依次搜索,因为前一行(或列)放置皇后之后就不能再放皇后了。这使得每次遍历都被限制在最多8格之内,从而形成了一颗”枝繁叶疏“的树,可以用回溯法等深度优先算法进行求解。
理论是美好的,实现的过程是残酷的,我用了若干小时编写这份代码,但最终很多理论都没有使用,所以写出来的代码没有什么特别的亮点,甚至自觉很丑陋,所以这篇只有算24的代码。不过在求解过程中,有一些小技巧还是值得和大家共同讨论的:
1、stack进行有序遍历:将y=0的8个位置push,在循环中把y=1-7的push(如果存在可能解),在循环开始输出y=7时的路径,这些路径就是解。
2、使用位棋盘,使用位棋盘可以大为加快记录皇后4向攻击位置(即不能再放皇后的位置)的计算速度:只需要读取mask[i]然后与当前的位棋盘进行按位与运算就得到了放置这个皇后之后的情况。用位棋盘速度非常块也非常节省空间,但是我在进行ULL类型|运算的时候,发现高32位没有被正确操作,可能是我的代码问题,也可能是编译器问题。可以用两个32位来代替,速度也比操作数组快很多。
因为关于8皇后这一经典问题,网上有很多代码,其中有一些质量比较高,很有参考价值,所以在这里就不再赘述(很好,隐藏我的丑陋代码成功)。
二、算24
这个NOI问题是这样的:
总时间限制: 3000ms 内存限制: 65536kB
描述
给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。 这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。 比如,对于5,,,,我们知道5 * ( – / ) = ,因此可以得到24。又比如,对于1,,,,我们怎么都不能得到24。
输入
输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
输出
对于每一组测试数据,输出一行,如果可以得到24,输出“YES”;否则,输出“NO”。
样例输入 样例输出
YES
NO
乍一看,好像挺简单……于是,拿过来就写:挨个拿出来和之前的结果运算(开始时把其中1个看作上次结果)、包括颠倒和括号,嘿……只得了5分。然后才想起来仔细分析题目,汗颜啊。这里包括这样一些运算过程:
只表示算式形式,而不代表顺序,逗号通配运算符:
a,b,c,d
(a,b),c,d
a,(b,c),d
a,b,(c,d)
(a,b,c),d
a,(b,c,d)
以及
(a,b),(c,d)
最后一种形式就是第一次5分的问题所在。”一个一个喂“的想法,导致最后一种形式被漏掉了。好吧,老老实实的:每次取2个得到一个计算结果,把计算结果作为一个待选运算数进行迭代:
//递归计算过程
void msearch(double val[],int valcnt){
if(flag){ //找到一种得出24的路径之后不再搜索其他。
return;
}
if(valcnt==){ //当只剩下一个数组元素,说明已经全部合并完成,这个元素就是最终结果。
if(abs(val[]-24.0)<=1e-){ //当结果等于24时,设置全局标记以结束递归。
flag=true;
}
}else{ //当数组元素有两个以上时,进行遍历,两两合并。并且把合并结果写入数组。
int i,j;
for(i=;i<valcnt;i++){ //用两个循环遍历全部的两两组合(参照冒泡法排序)。
for(j=i+;j<valcnt;j++){
//依次进行加减乘除以及减、除操作数的交换
nextval(val,valcnt,i,j,val[i]+val[j]);
nextval(val,valcnt,i,j,val[i]-val[j]);
nextval(val,valcnt,i,j,val[i]*val[j]);
if(val[i]!=){
nextval(val,valcnt,i,j,val[i]/val[j]);
}
nextval(val,valcnt,i,j,val[j]-val[i]);
if(val[j]!=){
nextval(val,valcnt,i,j,val[j]/val[i]);
}
}
}
}
}
这里有一行比较辣眼睛的代码:
if(abs(val[]-24.0)<=1e-){ //当结果等于24时,设置全局标记以结束递归。
如果换成1e-14还是5分……嘿,在我信心满满再次提交的时候,又是5分,于是对识海各种打补丁,结果……还是5分。死马当活马医吧,看看是不是精度问题:根据题意,最小值能到多少呢?3个10,1个1,1/10/10/10……之后进行若干次测试,这个”等于24“的阈值属于区间(0.001,1e-14]。这是动摇我对double精度的认识吗,以前写1e-15都没问题的?希望有大神指点一下。
好像没看见递归,为了在有限的C++知识范围内简化代码,我把它写到了nextval这个过程中,以下是算24的完整代码:
#include<iostream>
#include<cstring>
#include<cmath>
using namespace std;
bool flag;
void nextval(double val[],int valcnt,int idx1,int idx2,double lastresult);
//递归计算过程
void msearch(double val[],int valcnt){
if(flag){ //找到一种得出24的路径之后不再搜索其他。
return;
}
if(valcnt==){ //当只剩下一个数组元素,说明已经全部合并完成,这个元素就是最终结果。
if(abs(val[]-24.0)<=1e-){ //当结果等于24时,设置全局标记以结束递归。
flag=true;
}
}else{ //当数组元素有两个以上时,进行遍历,两两合并。并且把合并结果写入数组。
int i,j;
for(i=;i<valcnt;i++){ //用两个循环遍历全部的两两组合(参照冒泡法排序)。
for(j=i+;j<valcnt;j++){
//依次进行加减乘除以及减、除操作数的交换
nextval(val,valcnt,i,j,val[i]+val[j]);
nextval(val,valcnt,i,j,val[i]-val[j]);
nextval(val,valcnt,i,j,val[i]*val[j]);
if(val[i]!=){
nextval(val,valcnt,i,j,val[i]/val[j]);
}
nextval(val,valcnt,i,j,val[j]-val[i]);
if(val[j]!=){
nextval(val,valcnt,i,j,val[j]/val[i]);
}
}
}
}
}
//用取出两个数并加入结果的新数组继续递归
void nextval(double val[],int valcnt,int idx1,int idx2,double lastresult){
int i,newvalidx=;
double newval[valcnt-]; //新数组会比原来的少1(两个操作数标为一个结果)
for(i=;i<valcnt;i++){ //把没用到的复制到新数组
if(i!=idx1 && i!=idx2){
newval[newvalidx]=val[i];
newvalidx++;
}
}
newval[newvalidx]=lastresult; //把结果加入新数组
msearch(newval,newvalidx+); //用新数组递归搜索
}
int main(){
double val[];
while(true){
cin>>val[]>>val[]>>val[]>>val[];
if(val[]+val[]+val[]+val[]==){
break;
}
flag=false;
msearch(val,);
if(flag){
cout<<"YES"<<endl;
}else{
cout<<"NO"<<endl;
}
}
}
两个NOI题目的启迪8皇后和算24的更多相关文章
- ES6学习:两个面试题目--关于模板字符串
号称看完就能“让开发飞起来”,不过文中的两个面试题目的知识点并没包括在文中. https://www.jianshu.com/p/287e0bb867ae 文中并没有完整的知识点去完成上面的两道题,这 ...
- 九度oj 题目1254:N皇后问题
题目描述: N皇后问题,即在N*N的方格棋盘内放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在同一斜线上.因为皇后可以直走,横走和斜走如下图). 你的任务是,对 ...
- 从一个NOI题目再学习二分查找。
二分法的基本思路是对一个有序序列(递增递减都可以)查找时,测试一个中间下标处的值,若值比期待值小,则在更大的一侧进行查找(反之亦然),查找时再次二分.这比顺序访问要少很多访问量,效率很高. 设:low ...
- 九度oj 题目1140:八皇后
题目描述: 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题. 对于某个满足要求的 ...
- 近年NOI题目总结
NOI2015D1T1 题目大意:$T$ 组数据.在一个程序中有无数个变量 $x_i$.现在有 $n$ 条限制,形如 $x_i=x_j$ 或者 $x_i\ne x_j$.(对于每个限制 $i,j$ 给 ...
- 两道SQL题目
1.查询省内所有城市气温都大于35度的省份(表名:Temp) SELECT province FROM Temp WHERE province NOT IN ( SELECT province FRO ...
- Java继承的两道实验题目
设计一个表示二维平面上点的类Point,包含有表示坐标位置的Protect类型的成员变量 获取和设置x和y值的public方法 package classwork_6; public class Po ...
- 【noi 2.5_1789】算24(dfs)
最开始我想的是全排列+枚举符号和括号的方法,但是我自己倒腾了很久还是打不对,只好向他人请教.正解很机智--直接随意将几个数"捆绑"在一起,值存在其中一个数上,其他数标记不可再选,直 ...
- LeetCode:N-Queens I II(n皇后问题)
N-Queens The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no tw ...
随机推荐
- AngularJS--自定义指令和模板
一.自定义指令: 1. 先创建模块 var app=angular.module("myApp",[]); 2. 创建自定义指令 (directive后面的参数一:自定义指令 ...
- AngularJS 表单
AngularJS 表单是输入控件的集合. HTML控件 以下 HTML input 元素被称为 HTML 控件: input 元素 select 元素 button 元素 textarea 元素 H ...
- VR系统的组成
转载请声明转载地址:http://www.cnblogs.com/Rodolfo/,违者必究. 一个典型的虚拟现实系统主要由计算机.输入/输出设备.应用软件和数据库等部分组成. 1.计算机 在虚拟现实 ...
- 【Java EE 学习 50】【Spring学习第二天】【使用注解的DI实现】【spring中的继承】【动态代理伪hibernate实现】
一.使用注解的DI实现 1.@Resource 使用该注解能够实现引用型属性的DI实现,该注解能够根据属性名和属性类型自动给属性赋值.一般使用@Resource(name="student& ...
- 《DSP using MATLAB》示例Example5.10
代码: n = 0:10; x = 10*(0.8) .^ n; [xec, xoc] = circevod(x); %% -------------------------------------- ...
- python 函数传递方式
在python中方法传递的参数到底是值传递还是引用传递? 1. 首先需要知道python中变量的类型:Python的变量分为可变变量和不可变变量. 针对于不可变的类型比如string int def ...
- 表单验证——jquery validate使用说明【另一个教程】
[参考:http://www.tuicool.com/articles/y6fyme] jQuery Validate jQuery Validate 插件为表单提供了强大的验证功能,让客户端表单验证 ...
- JQuery上传插件Uploadify使用详解
本文转载http://www.cnblogs.com/oec2003/archive/2010/01/06/1640027.html Uploadify是JQuery的一个上传插件,实现的效果非常不错 ...
- [转]Nodejs基础中间件Connect
Nodejs基础中间件Connect 从零开始nodejs系列文章,将介绍如何利Javascript做为服务端脚本,通过Nodejs框架web开发.Nodejs框架是基于V8的引擎,是目前速度最快的J ...
- css position的使用
css position的使用 css 的 position 属性是用来设置元素的位置的,它还能设置一个元素出现在另一个元素的下层元素能用 top,bottom,left 和 right 属性设置位置 ...