论出于什么原因和目的,学习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的更多相关文章

  1. ES6学习:两个面试题目--关于模板字符串

    号称看完就能“让开发飞起来”,不过文中的两个面试题目的知识点并没包括在文中. https://www.jianshu.com/p/287e0bb867ae 文中并没有完整的知识点去完成上面的两道题,这 ...

  2. 九度oj 题目1254:N皇后问题

    题目描述: N皇后问题,即在N*N的方格棋盘内放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在同一斜线上.因为皇后可以直走,横走和斜走如下图). 你的任务是,对 ...

  3. 从一个NOI题目再学习二分查找。

    二分法的基本思路是对一个有序序列(递增递减都可以)查找时,测试一个中间下标处的值,若值比期待值小,则在更大的一侧进行查找(反之亦然),查找时再次二分.这比顺序访问要少很多访问量,效率很高. 设:low ...

  4. 九度oj 题目1140:八皇后

    题目描述: 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题. 对于某个满足要求的 ...

  5. 近年NOI题目总结

    NOI2015D1T1 题目大意:$T$ 组数据.在一个程序中有无数个变量 $x_i$.现在有 $n$ 条限制,形如 $x_i=x_j$ 或者 $x_i\ne x_j$.(对于每个限制 $i,j$ 给 ...

  6. 两道SQL题目

    1.查询省内所有城市气温都大于35度的省份(表名:Temp) SELECT province FROM Temp WHERE province NOT IN ( SELECT province FRO ...

  7. Java继承的两道实验题目

    设计一个表示二维平面上点的类Point,包含有表示坐标位置的Protect类型的成员变量 获取和设置x和y值的public方法 package classwork_6; public class Po ...

  8. 【noi 2.5_1789】算24(dfs)

    最开始我想的是全排列+枚举符号和括号的方法,但是我自己倒腾了很久还是打不对,只好向他人请教.正解很机智--直接随意将几个数"捆绑"在一起,值存在其中一个数上,其他数标记不可再选,直 ...

  9. 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 ...

随机推荐

  1. MyBatis的初始化方式

    1. 加载配置文件 public static void main(String[] args) throws IOException { //mybatis的配置文件 String resource ...

  2. yii中的cookie的发送和读取

    cookies: //新创建的cookie会从本地传到服务器上,然后从服务器获取. (1) cookie的发送 $cookies = Yii::$app->response->cookie ...

  3. volatile关键字详解

    本文系转载,原文链接:http://www.cnblogs.com/Chase/archive/2010/07/05/1771700.html,如有侵权,请联系我:534624117@qq.com 引 ...

  4. Revolving Digits(hdu 4333)

    题意:就是给你一个数字,然后把最后一个数字放到最前面去,经过几次变换后又回到原数字,问在这些数字中,比原数字小的,相等的,大的分别有多少个.比如341-->134-->413-->3 ...

  5. iOS10 SiriKit QQ适配详解

    原文连接 1. 概述 苹果在 iOS10 开放了 SiriKit 接口给第三方应用.目前,QQ已经率先适配了 Siri 的发消息和打电话功能.这意味着在 iOS10 中你可以直接告诉 Siri 让它帮 ...

  6. Note3 :《集体智慧编程》用户相似度计算

    欧几里德距离评价: 以经过人们一致评价的物品为坐标轴,然后将参与评价的人绘制到图上,并考察他们彼此之间的距离远近.计算出每一轴向上的差值,求平方之后再相加,最后对总和取平方根. # -*- codin ...

  7. hadoop入门(1)——hadoop概述

    一.hadoop生态系统特点 开源.社区活跃.涉及分布式存储和计算的整个生态系统.已得到企业界验证. hadoop1.0与2.0版本的比较: 1.0包含HDFS+MapReduce. 2.0包括HDF ...

  8. inner join on 和 where = 的区别!

    请看下面两条语句:select * from table1 inner join table2 on table1.id = table2.idselect * from table1,table2 ...

  9. Linux Tomcat 开机自启动的方法

    修改Tomcat/bin/startup.sh 为: export JAVA_HOME=/usr/java/j2sdk1.4.2_08 export CLASSPATH=$CLASSPATH:$JAV ...

  10. 将公司的主要项目从eclipse迁移到android studio for mac环境(2)

    1.将eclipse 的 代码迁移到android studio的过程中,现在已经有了一个未优化版本能够在android studio使用,这时候,只需要将已优化版本的代码中的libs  res  s ...