• 【2020-8-21】【数字游戏】【启发式搜索IDA*】


    有这么一个游戏: 
      写出一个1~N的排列a[i],然后每次将相邻两个数相加,构成新的序列,再对新序列进行这样的操作,显然每次构成的序列都比上一次的序列长度少1,直到只剩下一个数字位置。下面是一个例子: 
      3  1  2  4 
       4  3  6 
        7  9 
         16 
    最后得到16这样一个数字。 
      现在想要倒着玩这样一个游戏,如果知道N,知道最后得到的数字的大小sum,请你求出最初序列a[i],为1~N的一个排列。若答案有多种可能,则输出字典序最小的那一个。 (n<12)
    

      

    首先我们通过手算及暴力程序应该可以发现 

    设 sum 为最后一行的值

    总共有 1 行 则 sum=a1*1

    总共有 2 行 则 sum=a1*1+a2*1

    总共有 3 行 则 sum=a1*1+a2*2+a3*1

    总共有 4 行 则 sum=a1*1+a2*3+a3*3+a4*1

    不难发现sum等于二项式定理中的系数乘上第n个数

    所以对于这题我们只需要先预处理出二项式定理的系数 再对1~n进行dfs排列 适当加上一个剪枝就能AC

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    long long a[13],n,m,sum,b[13],last_num[13];
    bool k[13],p;
    void dfs(long long now,long long num,long long now_num)//当前位置 到当前位置的总和 当前位置的数值 
    {
        if(p)//取到解就不必再继续dfs 
        return ; 
        if(now==n)
        {
        if(num==m)
        {
         for(long long i=1;i<=n;i++)
        printf("%lld ",a[i]);
        p=1;
        return ;
        }
        else
        {
            k[now_num]=0;
            return ;
        }
        }
    
        for(long long i=1;i<=n;i++)
        {
            if(!k[i])
            {
                if(num+i*b[now+1]>m)//一个小剪枝 如果总和大于所要的解则不再扩展 
                {
                    k[now_num]=0;
                    return;
                }
                else
                {
                    k[i]=1;
                    a[now+1]=i;
                    dfs(now+1,num+i*b[now+1],i);
                    k[i]=0;//根据函数定义的回溯法求排列 
                }
            }
        }
    }
    int main()
    {
        scanf("%lld%lld",&n,&m);
        for(long long i=1;i<=n;i++)
        {
        b[i]=1;
        for(long long j=i-1;j>=1;j--)
        b[j]+=b[j-1];
        }
        for(long long i=n;i>=1;i--)
        {
           last_num[i]=b[i]+last_num[i+1];    
        } 
        dfs(0,0,0);
    }
    n<12

    对于原题中的n<12 我们的总和超过m就剪枝的做法是完全可以的

    但教练提了一个n<20 的问题 这时一个剪枝就显得不足了(只能拿20分)

    我们可以用IDA*的算法

    即在dfs扩展时计算未来可能的代价 如果代价超出能忍受的范围则剪枝

    我们考虑下面三个剪枝

    剪枝一:
    计算当前的“累加和”,若超出Sum,则没必要继续搜索

    剪枝二:
    计算当前的“累加和”+“未来的最小累加和”,若超出Sum,则没必要继续搜索

    剪枝三:
    计算当前的“累加和”+“未来的最大累加和”,若小于Sum,则没必要继续搜索

    其中剪枝 二 三 就是对未来的一个估价函数 

    #pragma GCC optimize(2)
    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    long long a[21],n,m,sum,b[21],sort_num[21],pai[21],minm,maxm;
    bool k[21],p,c;
    void yugu(long long now)
    {
        pai[0]=0;
        sort_num[0]=0;
        maxm=0;
        minm=0;
        for(long long i=n/2+1;i>=now;i--)
        pai[++pai[0]]=b[i];
        for(long long i=n/2+2;i<=n;i++)
        pai[++pai[0]]=b[i];   
        for(long long i=1;i<=n;i++)
        {
            if(!k[i])
            sort_num[++sort_num[0]]=i;
        }
            for(long long i=1;i<=sort_num[0];i++)
        {
            minm+=sort_num[i]*pai[i];
            maxm+=sort_num[i]*pai[sort_num[0]-i+1];
        }    
    }
    void dfs(long long now,long long num)
    {
    
        if(c)
        return ;
        if(num>m)
        return ;
        yugu(now+1);//进行预估代价
        if(num+maxm<m||num+minm>m)//判断是否可行剪枝
        return ; 
        if(now==n)
        {
        if(num==m)
        {
        for(long long i=1;i<=n;i++)
        printf("%lld ",a[i]);
        c=1;
        return ;
        }
        else
        return ;
        }
        for(long long i=1;i<=n;i++)
        {
            
            if(!c)
            if(!k[i])
            {        
               if(num+i*b[now+1]>m)
               continue;
               else
               {
                       k[i]=1;
                    a[now+1]=i;
                    dfs(now+1,num+i*b[now+1]);
                    k[i]=0;
               }
            }
        }
    }
    int main()
    {
    //    freopen("szyx.in","r",stdin);
    //    freopen("szyx.out","w",stdout);
        scanf("%lld%lld",&n,&m);
        for(long long i=1;i<=n;i++)
        {
        b[i]=1;
        for(long long j=i-1;j>=1;j--)
        b[j]+=b[j-1];
        }
        dfs(0,0);
        return 0;
    }
    启发式搜索 IDA*
  • 相关阅读:
    关于Mobx中装饰器语法的环境配置
    关于Java单例模式中懒汉式和饿汉式的两种类创建方法
    Java学习笔记之异常处理
    create-react-app脚手架的安装和目录结构介绍
    关于win10下JDK环境变量的配置以及关于JDK的一些说明
    关于Android studio的安装和配置问题
    关于通过ServletContext获取数据出现的http500的错误的解决方案
    关于form表单提交到Servlet的时候出现tomcat启动错误的解决方法
    Android网络编程系列之Volley总结
    Android网络编程系列之HTTP协议原理总结
  • 原文地址:https://www.cnblogs.com/dixiao/p/13541805.html
Copyright © 2020-2023  润新知