noi1789 算24 解题报告

分析:

半年前看到过这道题,当时是用了一段很暴力的代码骗过去的;今年学习数据结构时在深搜板块中发现这道题,这才醒悟它原来是个搜索题——套用了深搜的模板,带着许多投机取巧的要素和笨办法,总算是写好了。

这道题作为搜索题有它的独特之处——先不说减法和除法的方向性,光是计算结果又要插回去继续参与接下来的搜索这一点就令人心烦。为了解决这个问题,我设置了一个数组x[4],用于存储待处理的a[]中的数字:一开始时将四个数字读到a[]中,x[4]初始化为{0,1,2,3},然后深搜传递的参数i是针对x[]的,表示a[]中以x[0]~x[i]之间存储的内容为下标的数字是待使用的(包括尚未使用的初始数据和尚未使用的计算结果)。然后深搜的目标条件就是i==0,此时a[x[0]]中存储的数据就是根据之前的搜索路线得到的结果,把它和24比较,相等就退出搜索;不相等就回溯,继续搜索。

在搜索的过程中,我使用了j和k两个指针指向x[0]~x[i]中的任意两个不同的数字,分别将它们进行加减乘除四种运算(这里我偷了个懒,因为j和k两个指针是任意指的,所以就不用考虑减法和除法的方向性了。)运算后将结果存在a[x[j]]中(在这之前先保存下a[x[j]]的值,方面后面回溯)。这样,a[x[j]]中存放着尚未使用的数据,a[x[k]]中存放着已使用过的数据,a[x[i]]中的数据尚未使用(k==i时也不影响程序),而k≤i,所以我交换了x[k]和x[i]的值,保证a[]中以x[0]到x[i-1]中存放的值为下标的数据都是尚待使用的。然后将i-1作为参数传入下一层搜索。搜索回来后再交换回x[i]和x[k],将一开始存放的a[x[j]]的值再放回去,将一切恢复原状,进行下一轮搜索。


具体代码如下:

#include<stdio.h>
using namespace std;

double a[4]={0};//用于存储计算过程中的数字 
int flag;//能否凑出24的标记 
int x[4]={0,1,2,3}; //x[i]中存储的数为a[]中待使用的数
                    //使用时为for(int i=0;i<dfs的传递参数;i++)a[x[i]];
double abs(double a); 
void dfs(int i);;//表示以x[0]~x[i]中的数字为下标的a[]的数字尚未用过

int main(){
    while(true){
        flag=0;
        for(int i=0;i<4;i++)
            scanf("%lf",&a[i]);    
        if(!a[0] && !a[1] && !a[2] && !a[3]) return 0;    
        dfs(3);//表示以x[0]~x[3]中的数字为下标的a[](也就是全部)的数字尚未用过 
        printf("%s\n",flag?"YES":"NO");
    }
}
void dfs(int i){
    if(i==0){//表示此时只有a[x[0]]中的数字尚未用过——这是计算的最后结果 
        if(abs(a[x[0]]-24)<0.0001)
            flag=1;
        return;
    }
    for(int j=0;j<=i;j++)
        for(int k=0;k<=i;k++){
            if(j==k)continue;    //j和k是两个指针,指向a[]中两个不同的数字,用于计算 
             for(int l=1;l<=4;l++){//一共加减乘除四种情况,本来减和除有方向的区别,但因为j和k的两个指针是取了所有情况的,所以就不用了 
                 double tmp=a[x[j]];//a[x[j]]和a[x[k]]的运算结果将存储在a[x[j]]中进行之后的搜索,为了回溯,这里先保存一下a[x[j]]的值 
                 switch(l){
                     case 1:a[x[j]]=a[x[j]]+a[x[k]];        break;
                    case 2:a[x[j]]=a[x[j]]-a[x[k]];        break;
                    case 3:a[x[j]]=a[x[j]]*a[x[k]];        break;
                    case 4:a[x[j]]=a[x[j]]/a[x[k]];        break;
                //    case 5:a[x[j]]=a[x[k]]-a[x[j]];        break;
                //    case 6:a[x[j]]=a[x[k]]-a[x[j]];        break;
                 }
                {int tmpn=x[k];
                     x[k]=x[i];
                     x[i]=tmpn;}//a[x[k]]在上面已经用过,不再需要,而a[x[i]]尚未用到(i==k时同样),故交换x[k]和x[i],保证在x[i]以前的所有x[]对应的a[]都是尚待使用的 
                dfs(i-1);
                {int tmpn=x[k];
                     x[k]=x[i];
                     x[i]=tmpn;}//交换回来,恢复原状 
                a[x[j]]=tmp;//恢复原状,开始下一轮搜索 
                if(flag)
                    return;
             }
        }
    return;
}
double abs(double a){
    if(a>0)return a;
    return -a;


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值