zoukankan      html  css  js  c++  java
  • sss

    <更新提示>

    <第一次更新>


    <正文>

    Power Hungry Cows(POJ 1945)

    Description

    FJ的奶牛想要快速计算整数P的幂 (1 <= P <=20,000),它们需要你的帮助。因为计算极大数的幂,所以它们同一时间仅能使用2个存储器,每个存储器可记录某个结果值。 第一件工作是初始化存储器内的值一个为底数x, 另一个为1。 奶牛可以相乘或相除2个存储器中的值,并把结果存在其中某个存储器内,但所有存储的结果必须是整数。 例如, 如果他们想计算x^31, 一种计算方法是:

                                       WV1  WV2
                                开始:   x    1
    存储器1和存储器1相乘,结果存于存储器2:   x   x^2
    存储器2和存储器2相乘,结果存于存储器2:   x   x^4
    存储器2和存储器2相乘,结果存于存储器2:   x   x^8
    存储器2和存储器2相乘,结果存于存储器2:   x   x^16
    存储器2和存储器2相乘,结果存于存储器2:   x   x^32
    存储器2除以存储器1,结果存于存储器2:     x   x^31
    

    因此, x^31可以通过6次计算得出。给出要计算的幂次,要求求出最少需要几次计算。

    Input Format

    仅一个整数: P。

    Output Format

    仅一个整数:最少计算次数。

    Sample Input

    31
    

    Sample Output

    6
    

    解析

    如果考虑(12)种转移方式,花费均为(1),那么直接(bfs)是可以的,但是状态将达到(12^{ans})之多,实际数据中,(ans)最大将达到(18)

    可以考虑设计一个估价函数,将(bfs)算法直接改进为(A^*)的启发式搜索算法。这个是比较容易想到的。设置状态((a,b))代表第一个存储器指数为(a),第二个存储器指数为(b),令

    [f(state)=min_{max(a,b)*2^{cnt}geq p}{cnt} ]

    (max(a,b)geq p)时,(f(state)=0)。对于第一个式子,很好理解,当(a)(b)最大值小于(p)时,最小的计算次数就是成倍增长所要的次数,由于最后可能还需要减法微调,可以保证(f(state)geq g(state))。而对于第二个式子,由于减法我们无法保证,取(f(state)=0)即可。

    这样的(A^*)算法可以得到(42)分。

    剩下的测试点的错误的原因是(MLE+TLE),但导致它们原因是相同的:状态太多。

    对于解决这个问题,最好的办法是剪枝,删去不优或无用状态。

    可以考虑如下几个剪枝:

    (1.)((a,b))((b,a))是一样的,令(a>b),删去第二种状态
    (2.)((a,a))这个状态一定不如((a,b)),自乘自除可以达到一样的效果,直接删去
    (3.)形如((a,0))的状态,当(a>p)时,无解,直接删去
    (4.)(p\%gcd(a,b) ≠ 0)时,状态((a,b))无解,直接删去
    (5.)(a>p*2)时,这个状态即使能得到解,计算次数也一定不是最优的,直接删去

    加上一堆剪枝后,就能得到(81)分的高分了。

    还有两个测试点还因为(TLE),而我们的思路(A^*)算法,记录计算次数的数组直接开二维是不行的,又因为状态稀疏,所以选择使用(map)。但是现在使用(map)的常数太大,需要改用(hash)表来存储。

    这样就能完美解决本题了。

    以下是几个有关(A^*)需要注意的地方:

    (1.)一切(A^*)算法基于优先队列(bfs)算法,优先队列(bfs)要做的一切操作,(A^*)都要做,最容易被忽视的就是 "一个状态第一次被取出时就是最优解,所以一个状态只会拓展一次,但是一个状态的最优解可能会被更新多次",所以要开数组((map)(hash))记录到这个状态的最小花费。
    (2.)(A^*)算法堆的排序关键字是当前花费加上未来预估花费,但是当两个状态的当前花费加上未来花费相同时,以未来花费为第二关键字,这对时间影响极大。

    (Code:)

    #include<cstdio>
    #include<iostream>
    #include<queue>
    using namespace std;
    #define mset(name,val) memset(name,val,sizeof name)
    #define filein(str) freopen(str".in","r",stdin)
    #define fileout(str) freopen(str".out","w",stdout) 
    const int P=20020,SIZE=1000080;
    int p,Head[P],cnt;
    struct node
    {
        int next,val,s;
    };
    struct state
    {
        int a,b,f,s;
        inline void fcost(void)
        {
            this->f = 0;
            int Max = max( this->a , this->b );
            while ( Max<p ) Max<<=1 , (this->f)++ ;
        }
        bool operator < (const state t)const
        {
            return this->s+this->f == t.s+t.f ? this->f < t.f : this->s+this->f < t.s+t.f ;
        }
        bool operator > (const state t)const
        {
            return this->s+this->f == t.s+t.f ? this->f > t.f : this->s+this->f > t.s+t.f ;
        }
    };
    priority_queue < state,vector<state>,greater<state> > Heap;
    state Begin;
    node List[SIZE];
    inline void input(void)
    {
        scanf("%d",&p);
        Begin=(state){1,0,0,0};
    }
    inline int Hash(int x,int y)
    {
        return (int)(((long long)x*2023%SIZE+(long long)y*1926%SIZE)%SIZE);
    }
    inline bool retrieval(int val,int s)
    {
        int index=val%P;
        for (int i=Head[index];i;i=List[i].next)
        {
            if (List[i].val==val)
            {
                if (List[i].s<=s)return true;
                List[i].s=s;return false;
            }
        }
        List[++cnt]=(node){Head[index],val,s};
        Head[index]=cnt;
        return false;
    }
    inline int gcd(int a,int b)
    {
        return b==0 ? a : gcd( b , a%b );
    }
    inline bool check(int x,int y)
    {
        return x==y || ( x>p && y==0 ) || p % gcd(x,y) || x>2*p ;
    }
    inline void transfer(int x,int y,int t)
    {
        if (x<y) swap(x,y);
        if (check(x,y))return;
        if (retrieval(Hash(x,y),t+1))return;
        state next=(state){x,y,0,t+1};
        next.fcost();
        Heap.push(next);
    }
    inline int Astar(void)
    {
        Heap.push(Begin);
        while (!Heap.empty())
        {
            state t=Heap.top();
            Heap.pop();
            if (t.a==p||t.b==p)return t.s;
            int temp[2]={t.a,t.b};
            for (int i=0;i<2;i++)
            {
                for (int j=i;j<2;j++)
                {
                    for (int k=0;k<2;k++)
                    {
                        if (!k)
                            transfer(temp[i]+temp[j],temp[1],t.s);
                        if (k)
                            transfer(temp[0],temp[i]+temp[j],t.s);
                    }
                }
            }
            transfer(temp[0],temp[0]-temp[1],t.s);
            transfer(temp[0]-temp[1],temp[1],t.s);
        }
    }
    int main(void)
    {
        filein("power");
        fileout("power");
        input();
        printf("%d
    ",Astar());
        return 0;
    }
    

    <后记>

  • 相关阅读:
    keep-alive的深入理解与使用(配合router-view缓存整个路由页面)
    vue无法自动打开浏览器
    解决vue页面刷新或者后退参数丢失的问题
    vue 跳转并传参,实现数据实时更新
    Struts2 有关于无法正常的使用通配符
    有关于java反编译工具的使用
    Action名称的搜索顺序
    Struts2 的 值栈和ActionContext
    在Action 中访问web资源
    oracle 创建database Link
  • 原文地址:https://www.cnblogs.com/Parsnip/p/10640359.html
Copyright © 2011-2022 走看看